Thu, 01 May 2014 11:35:02 -0700
8036942: javac generates incorrect exception table for multi-catch statements inside a lambda
Summary: Union type info lost and also union type is not processed by TreeMaker.Type -- address by using existing tree, thus by-passing such issues.
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 tree.init = translate(tree.init);
442 tree.sym = (VarSymbol) lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym);
443 result = tree;
444 } else if (context != null && lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
445 JCExpression init = translate(tree.init);
446 VarSymbol xsym = (VarSymbol)lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
447 int prevPos = make.pos;
448 try {
449 result = make.at(tree).VarDef(xsym, init);
450 } finally {
451 make.at(prevPos);
452 }
453 // Replace the entered symbol for this variable
454 Scope sc = tree.sym.owner.members();
455 if (sc != null) {
456 sc.remove(tree.sym);
457 sc.enter(xsym);
458 }
459 } else {
460 super.visitVarDef(tree);
461 }
462 }
464 // </editor-fold>
466 // <editor-fold defaultstate="collapsed" desc="Translation helper methods">
468 private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) {
469 return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ?
470 makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) :
471 makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally);
472 }
474 private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) {
475 Type restype = lambdaMethodDecl.type.getReturnType();
476 boolean isLambda_void = expr.type.hasTag(VOID);
477 boolean isTarget_void = restype.hasTag(VOID);
478 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
479 int prevPos = make.pos;
480 try {
481 if (isTarget_void) {
482 //target is void:
483 // BODY;
484 JCStatement stat = make.at(expr).Exec(expr);
485 return make.Block(0, List.<JCStatement>of(stat));
486 } else if (isLambda_void && isTarget_Void) {
487 //void to Void conversion:
488 // BODY; return null;
489 ListBuffer<JCStatement> stats = new ListBuffer<>();
490 stats.append(make.at(expr).Exec(expr));
491 stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
492 return make.Block(0, stats.toList());
493 } else {
494 //non-void to non-void conversion:
495 // return (TYPE)BODY;
496 JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype);
497 return make.at(retExpr).Block(0, List.<JCStatement>of(make.Return(retExpr)));
498 }
499 } finally {
500 make.at(prevPos);
501 }
502 }
504 private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) {
505 final Type restype = lambdaMethodDecl.type.getReturnType();
506 final boolean isTarget_void = restype.hasTag(VOID);
507 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
509 class LambdaBodyTranslator extends TreeTranslator {
511 @Override
512 public void visitClassDef(JCClassDecl tree) {
513 //do NOT recurse on any inner classes
514 result = tree;
515 }
517 @Override
518 public void visitLambda(JCLambda tree) {
519 //do NOT recurse on any nested lambdas
520 result = tree;
521 }
523 @Override
524 public void visitReturn(JCReturn tree) {
525 boolean isLambda_void = tree.expr == null;
526 if (isTarget_void && !isLambda_void) {
527 //Void to void conversion:
528 // { TYPE $loc = RET-EXPR; return; }
529 VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym);
530 JCVariableDecl varDef = make.VarDef(loc, tree.expr);
531 result = make.Block(0, List.<JCStatement>of(varDef, make.Return(null)));
532 } else if (!isTarget_void || !isLambda_void) {
533 //non-void to non-void conversion:
534 // return (TYPE)RET-EXPR;
535 tree.expr = transTypes.coerce(attrEnv, tree.expr, restype);
536 result = tree;
537 } else {
538 result = tree;
539 }
541 }
542 }
544 JCBlock trans_block = new LambdaBodyTranslator().translate(block);
545 if (completeNormally && isTarget_Void) {
546 //there's no return statement and the lambda (possibly inferred)
547 //return type is java.lang.Void; emit a synthetic return statement
548 trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
549 }
550 return trans_block;
551 }
553 private JCMethodDecl makeDeserializeMethod(Symbol kSym) {
554 ListBuffer<JCCase> cases = new ListBuffer<>();
555 ListBuffer<JCBreak> breaks = new ListBuffer<>();
556 for (Map.Entry<String, ListBuffer<JCStatement>> entry : kInfo.deserializeCases.entrySet()) {
557 JCBreak br = make.Break(null);
558 breaks.add(br);
559 List<JCStatement> stmts = entry.getValue().append(br).toList();
560 cases.add(make.Case(make.Literal(entry.getKey()), stmts));
561 }
562 JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
563 for (JCBreak br : breaks) {
564 br.target = sw;
565 }
566 JCBlock body = make.Block(0L, List.<JCStatement>of(
567 sw,
568 make.Throw(makeNewClass(
569 syms.illegalArgumentExceptionType,
570 List.<JCExpression>of(make.Literal("Invalid lambda deserialization"))))));
571 JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()),
572 names.deserializeLambda,
573 make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym),
574 List.<JCTypeParameter>nil(),
575 List.of(make.VarDef(kInfo.deserParamSym, null)),
576 List.<JCExpression>nil(),
577 body,
578 null);
579 deser.sym = kInfo.deserMethodSym;
580 deser.type = kInfo.deserMethodSym.type;
581 //System.err.printf("DESER: '%s'\n", deser);
582 return deser;
583 }
585 /** Make an attributed class instance creation expression.
586 * @param ctype The class type.
587 * @param args The constructor arguments.
588 * @param cons The constructor symbol
589 */
590 JCNewClass makeNewClass(Type ctype, List<JCExpression> args, Symbol cons) {
591 JCNewClass tree = make.NewClass(null,
592 null, make.QualIdent(ctype.tsym), args, null);
593 tree.constructor = cons;
594 tree.type = ctype;
595 return tree;
596 }
598 /** Make an attributed class instance creation expression.
599 * @param ctype The class type.
600 * @param args The constructor arguments.
601 */
602 JCNewClass makeNewClass(Type ctype, List<JCExpression> args) {
603 return makeNewClass(ctype, args,
604 rs.resolveConstructor(null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil()));
605 }
607 private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym,
608 DiagnosticPosition pos, List<Object> staticArgs, MethodType indyType) {
609 String functionalInterfaceClass = classSig(targetType);
610 String functionalInterfaceMethodName = samSym.getSimpleName().toString();
611 String functionalInterfaceMethodSignature = typeSig(types.erasure(samSym.type));
612 String implClass = classSig(types.erasure(refSym.owner.type));
613 String implMethodName = refSym.getQualifiedName().toString();
614 String implMethodSignature = typeSig(types.erasure(refSym.type));
616 JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType), make.Literal(implMethodKind));
617 ListBuffer<JCExpression> serArgs = new ListBuffer<>();
618 int i = 0;
619 for (Type t : indyType.getParameterTypes()) {
620 List<JCExpression> indexAsArg = new ListBuffer<JCExpression>().append(make.Literal(i)).toList();
621 List<Type> argTypes = new ListBuffer<Type>().append(syms.intType).toList();
622 serArgs.add(make.TypeCast(types.erasure(t), deserGetter("getCapturedArg", syms.objectType, argTypes, indexAsArg)));
623 ++i;
624 }
625 JCStatement stmt = make.If(
626 deserTest(deserTest(deserTest(deserTest(deserTest(
627 kindTest,
628 "getFunctionalInterfaceClass", functionalInterfaceClass),
629 "getFunctionalInterfaceMethodName", functionalInterfaceMethodName),
630 "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature),
631 "getImplClass", implClass),
632 "getImplMethodSignature", implMethodSignature),
633 make.Return(makeIndyCall(
634 pos,
635 syms.lambdaMetafactory,
636 names.altMetafactory,
637 staticArgs, indyType, serArgs.toList(), samSym.name)),
638 null);
639 ListBuffer<JCStatement> stmts = kInfo.deserializeCases.get(implMethodName);
640 if (stmts == null) {
641 stmts = new ListBuffer<>();
642 kInfo.deserializeCases.put(implMethodName, stmts);
643 }
644 /****
645 System.err.printf("+++++++++++++++++\n");
646 System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass);
647 System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName);
648 System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature);
649 System.err.printf("*implMethodKind: %d\n", implMethodKind);
650 System.err.printf("*implClass: '%s'\n", implClass);
651 System.err.printf("*implMethodName: '%s'\n", implMethodName);
652 System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature);
653 ****/
654 stmts.append(stmt);
655 }
657 private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) {
658 JCBinary testExpr = make.Binary(JCTree.Tag.EQ, arg1, arg2);
659 testExpr.operator = rs.resolveBinaryOperator(null, JCTree.Tag.EQ, attrEnv, argType, argType);
660 testExpr.setType(syms.booleanType);
661 return testExpr;
662 }
664 private JCExpression deserTest(JCExpression prev, String func, String lit) {
665 MethodType eqmt = new MethodType(List.of(syms.objectType), syms.booleanType, List.<Type>nil(), syms.methodClass);
666 Symbol eqsym = rs.resolveQualifiedMethod(null, attrEnv, syms.objectType, names.equals, List.of(syms.objectType), List.<Type>nil());
667 JCMethodInvocation eqtest = make.Apply(
668 List.<JCExpression>nil(),
669 make.Select(deserGetter(func, syms.stringType), eqsym).setType(eqmt),
670 List.<JCExpression>of(make.Literal(lit)));
671 eqtest.setType(syms.booleanType);
672 JCBinary compound = make.Binary(JCTree.Tag.AND, prev, eqtest);
673 compound.operator = rs.resolveBinaryOperator(null, JCTree.Tag.AND, attrEnv, syms.booleanType, syms.booleanType);
674 compound.setType(syms.booleanType);
675 return compound;
676 }
678 private JCExpression deserGetter(String func, Type type) {
679 return deserGetter(func, type, List.<Type>nil(), List.<JCExpression>nil());
680 }
682 private JCExpression deserGetter(String func, Type type, List<Type> argTypes, List<JCExpression> args) {
683 MethodType getmt = new MethodType(argTypes, type, List.<Type>nil(), syms.methodClass);
684 Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.<Type>nil());
685 return make.Apply(
686 List.<JCExpression>nil(),
687 make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt),
688 args).setType(type);
689 }
691 /**
692 * Create new synthetic method with given flags, name, type, owner
693 */
694 private MethodSymbol makePrivateSyntheticMethod(long flags, Name name, Type type, Symbol owner) {
695 return new MethodSymbol(flags | SYNTHETIC | PRIVATE, name, type, owner);
696 }
698 /**
699 * Create new synthetic variable with given flags, name, type, owner
700 */
701 private VarSymbol makeSyntheticVar(long flags, String name, Type type, Symbol owner) {
702 return makeSyntheticVar(flags, names.fromString(name), type, owner);
703 }
705 /**
706 * Create new synthetic variable with given flags, name, type, owner
707 */
708 private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) {
709 return new VarSymbol(flags | SYNTHETIC, name, type, owner);
710 }
712 /**
713 * Set varargsElement field on a given tree (must be either a new class tree
714 * or a method call tree)
715 */
716 private void setVarargsIfNeeded(JCTree tree, Type varargsElement) {
717 if (varargsElement != null) {
718 switch (tree.getTag()) {
719 case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break;
720 case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break;
721 default: throw new AssertionError();
722 }
723 }
724 }
726 /**
727 * Convert method/constructor arguments by inserting appropriate cast
728 * as required by type-erasure - this is needed when bridging a lambda/method
729 * reference, as the bridged signature might require downcast to be compatible
730 * with the generated signature.
731 */
732 private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) {
733 Assert.check(meth.kind == Kinds.MTH);
734 List<Type> formals = types.erasure(meth.type).getParameterTypes();
735 if (varargsElement != null) {
736 Assert.check((meth.flags() & VARARGS) != 0);
737 }
738 return transTypes.translateArgs(args, formals, varargsElement, attrEnv);
739 }
741 // </editor-fold>
743 /**
744 * Generate an adapter method "bridge" for a method reference which cannot
745 * be used directly.
746 */
747 private class MemberReferenceBridger {
749 private final JCMemberReference tree;
750 private final ReferenceTranslationContext localContext;
751 private final ListBuffer<JCExpression> args = new ListBuffer<>();
752 private final ListBuffer<JCVariableDecl> params = new ListBuffer<>();
754 MemberReferenceBridger(JCMemberReference tree, ReferenceTranslationContext localContext) {
755 this.tree = tree;
756 this.localContext = localContext;
757 }
759 /**
760 * Generate the bridge
761 */
762 JCMethodDecl bridge() {
763 int prevPos = make.pos;
764 try {
765 make.at(tree);
766 Type samDesc = localContext.bridgedRefSig();
767 List<Type> samPTypes = samDesc.getParameterTypes();
769 //an extra argument is prepended to the signature of the bridge in case
770 //the member reference is an instance method reference (in which case
771 //the receiver expression is passed to the bridge itself).
772 Type recType = null;
773 switch (tree.kind) {
774 case IMPLICIT_INNER:
775 recType = tree.sym.owner.type.getEnclosingType();
776 break;
777 case BOUND:
778 recType = tree.getQualifierExpression().type;
779 break;
780 case UNBOUND:
781 recType = samPTypes.head;
782 samPTypes = samPTypes.tail;
783 break;
784 }
786 //generate the parameter list for the bridged member reference - the
787 //bridge signature will match the signature of the target sam descriptor
789 VarSymbol rcvr = (recType == null)
790 ? null
791 : addParameter("rec$", recType, false);
793 List<Type> refPTypes = tree.sym.type.getParameterTypes();
794 int refSize = refPTypes.size();
795 int samSize = samPTypes.size();
796 // Last parameter to copy from referenced method
797 int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize;
799 List<Type> l = refPTypes;
800 // Use parameter types of the referenced method, excluding final var args
801 for (int i = 0; l.nonEmpty() && i < last; ++i) {
802 addParameter("x$" + i, l.head, true);
803 l = l.tail;
804 }
805 // Flatten out the var args
806 for (int i = last; i < samSize; ++i) {
807 addParameter("xva$" + i, tree.varargsElement, true);
808 }
810 //generate the bridge method declaration
811 JCMethodDecl bridgeDecl = make.MethodDef(make.Modifiers(localContext.bridgeSym.flags()),
812 localContext.bridgeSym.name,
813 make.QualIdent(samDesc.getReturnType().tsym),
814 List.<JCTypeParameter>nil(),
815 params.toList(),
816 tree.sym.type.getThrownTypes() == null
817 ? List.<JCExpression>nil()
818 : make.Types(tree.sym.type.getThrownTypes()),
819 null,
820 null);
821 bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym;
822 bridgeDecl.type = localContext.bridgeSym.type =
823 types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
825 //bridge method body generation - this can be either a method call or a
826 //new instance creation expression, depending on the member reference kind
827 JCExpression bridgeExpr = (tree.getMode() == ReferenceMode.INVOKE)
828 ? bridgeExpressionInvoke(makeReceiver(rcvr))
829 : bridgeExpressionNew();
831 //the body is either a return expression containing a method call,
832 //or the method call itself, depending on whether the return type of
833 //the bridge is non-void/void.
834 bridgeDecl.body = makeLambdaExpressionBody(bridgeExpr, bridgeDecl);
836 return bridgeDecl;
837 } finally {
838 make.at(prevPos);
839 }
840 }
841 //where
842 private JCExpression makeReceiver(VarSymbol rcvr) {
843 if (rcvr == null) return null;
844 JCExpression rcvrExpr = make.Ident(rcvr);
845 Type rcvrType = tree.sym.enclClass().type;
846 if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) {
847 rcvrExpr = make.TypeCast(make.Type(rcvrType), rcvrExpr).setType(rcvrType);
848 }
849 return rcvrExpr;
850 }
852 /**
853 * determine the receiver of the bridged method call - the receiver can
854 * be either the synthetic receiver parameter or a type qualifier; the
855 * original qualifier expression is never used here, as it might refer
856 * to symbols not available in the static context of the bridge
857 */
858 private JCExpression bridgeExpressionInvoke(JCExpression rcvr) {
859 JCExpression qualifier =
860 tree.sym.isStatic() ?
861 make.Type(tree.sym.owner.type) :
862 (rcvr != null) ?
863 rcvr :
864 tree.getQualifierExpression();
866 //create the qualifier expression
867 JCFieldAccess select = make.Select(qualifier, tree.sym.name);
868 select.sym = tree.sym;
869 select.type = tree.sym.erasure(types);
871 //create the method call expression
872 JCExpression apply = make.Apply(List.<JCExpression>nil(), select,
873 convertArgs(tree.sym, args.toList(), tree.varargsElement)).
874 setType(tree.sym.erasure(types).getReturnType());
876 apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType());
877 setVarargsIfNeeded(apply, tree.varargsElement);
878 return apply;
879 }
881 /**
882 * the enclosing expression is either 'null' (no enclosing type) or set
883 * to the first bridge synthetic parameter
884 */
885 private JCExpression bridgeExpressionNew() {
886 if (tree.kind == ReferenceKind.ARRAY_CTOR) {
887 //create the array creation expression
888 JCNewArray newArr = make.NewArray(
889 make.Type(types.elemtype(tree.getQualifierExpression().type)),
890 List.of(make.Ident(params.first())),
891 null);
892 newArr.type = tree.getQualifierExpression().type;
893 return newArr;
894 } else {
895 JCExpression encl = null;
896 switch (tree.kind) {
897 case UNBOUND:
898 case IMPLICIT_INNER:
899 encl = make.Ident(params.first());
900 }
902 //create the instance creation expression
903 JCNewClass newClass = make.NewClass(encl,
904 List.<JCExpression>nil(),
905 make.Type(tree.getQualifierExpression().type),
906 convertArgs(tree.sym, args.toList(), tree.varargsElement),
907 null);
908 newClass.constructor = tree.sym;
909 newClass.constructorType = tree.sym.erasure(types);
910 newClass.type = tree.getQualifierExpression().type;
911 setVarargsIfNeeded(newClass, tree.varargsElement);
912 return newClass;
913 }
914 }
916 private VarSymbol addParameter(String name, Type p, boolean genArg) {
917 VarSymbol vsym = new VarSymbol(0, names.fromString(name), p, localContext.bridgeSym);
918 params.append(make.VarDef(vsym, null));
919 if (genArg) {
920 args.append(make.Ident(vsym));
921 }
922 return vsym;
923 }
924 }
926 /**
927 * Bridges a member reference - this is needed when:
928 * * Var args in the referenced method need to be flattened away
929 * * super is used
930 */
931 private void bridgeMemberReference(JCMemberReference tree, ReferenceTranslationContext localContext) {
932 kInfo.addMethod(new MemberReferenceBridger(tree, localContext).bridge());
933 }
935 private MethodType typeToMethodType(Type mt) {
936 Type type = types.erasure(mt);
937 return new MethodType(type.getParameterTypes(),
938 type.getReturnType(),
939 type.getThrownTypes(),
940 syms.methodClass);
941 }
943 /**
944 * Generate an indy method call to the meta factory
945 */
946 private JCExpression makeMetafactoryIndyCall(TranslationContext<?> context,
947 int refKind, Symbol refSym, List<JCExpression> indy_args) {
948 JCFunctionalExpression tree = context.tree;
949 //determine the static bsm args
950 MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
951 List<Object> staticArgs = List.<Object>of(
952 typeToMethodType(samSym.type),
953 new Pool.MethodHandle(refKind, refSym, types),
954 typeToMethodType(tree.getDescriptorType(types)));
956 //computed indy arg types
957 ListBuffer<Type> indy_args_types = new ListBuffer<>();
958 for (JCExpression arg : indy_args) {
959 indy_args_types.append(arg.type);
960 }
962 //finally, compute the type of the indy call
963 MethodType indyType = new MethodType(indy_args_types.toList(),
964 tree.type,
965 List.<Type>nil(),
966 syms.methodClass);
968 Name metafactoryName = context.needsAltMetafactory() ?
969 names.altMetafactory : names.metafactory;
971 if (context.needsAltMetafactory()) {
972 ListBuffer<Object> markers = new ListBuffer<>();
973 for (Type t : tree.targets.tail) {
974 if (t.tsym != syms.serializableType.tsym) {
975 markers.append(t.tsym);
976 }
977 }
978 int flags = context.isSerializable() ? FLAG_SERIALIZABLE : 0;
979 boolean hasMarkers = markers.nonEmpty();
980 boolean hasBridges = context.bridges.nonEmpty();
981 if (hasMarkers) {
982 flags |= FLAG_MARKERS;
983 }
984 if (hasBridges) {
985 flags |= FLAG_BRIDGES;
986 }
987 staticArgs = staticArgs.append(flags);
988 if (hasMarkers) {
989 staticArgs = staticArgs.append(markers.length());
990 staticArgs = staticArgs.appendList(markers.toList());
991 }
992 if (hasBridges) {
993 staticArgs = staticArgs.append(context.bridges.length() - 1);
994 for (Symbol s : context.bridges) {
995 Type s_erasure = s.erasure(types);
996 if (!types.isSameType(s_erasure, samSym.erasure(types))) {
997 staticArgs = staticArgs.append(s.erasure(types));
998 }
999 }
1000 }
1001 if (context.isSerializable()) {
1002 int prevPos = make.pos;
1003 try {
1004 make.at(kInfo.clazz);
1005 addDeserializationCase(refKind, refSym, tree.type, samSym,
1006 tree, staticArgs, indyType);
1007 } finally {
1008 make.at(prevPos);
1009 }
1010 }
1011 }
1013 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args, samSym.name);
1014 }
1016 /**
1017 * Generate an indy method call with given name, type and static bootstrap
1018 * arguments types
1019 */
1020 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
1021 List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs,
1022 Name methName) {
1023 int prevPos = make.pos;
1024 try {
1025 make.at(pos);
1026 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
1027 syms.stringType,
1028 syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs));
1030 Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site,
1031 bsmName, bsm_staticArgs, List.<Type>nil());
1033 DynamicMethodSymbol dynSym =
1034 new DynamicMethodSymbol(methName,
1035 syms.noSymbol,
1036 bsm.isStatic() ?
1037 ClassFile.REF_invokeStatic :
1038 ClassFile.REF_invokeVirtual,
1039 (MethodSymbol)bsm,
1040 indyType,
1041 staticArgs.toArray());
1043 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName);
1044 qualifier.sym = dynSym;
1045 qualifier.type = indyType.getReturnType();
1047 JCMethodInvocation proxyCall = make.Apply(List.<JCExpression>nil(), qualifier, indyArgs);
1048 proxyCall.type = indyType.getReturnType();
1049 return proxyCall;
1050 } finally {
1051 make.at(prevPos);
1052 }
1053 }
1054 //where
1055 private List<Type> bsmStaticArgToTypes(List<Object> args) {
1056 ListBuffer<Type> argtypes = new ListBuffer<>();
1057 for (Object arg : args) {
1058 argtypes.append(bsmStaticArgToType(arg));
1059 }
1060 return argtypes.toList();
1061 }
1063 private Type bsmStaticArgToType(Object arg) {
1064 Assert.checkNonNull(arg);
1065 if (arg instanceof ClassSymbol) {
1066 return syms.classType;
1067 } else if (arg instanceof Integer) {
1068 return syms.intType;
1069 } else if (arg instanceof Long) {
1070 return syms.longType;
1071 } else if (arg instanceof Float) {
1072 return syms.floatType;
1073 } else if (arg instanceof Double) {
1074 return syms.doubleType;
1075 } else if (arg instanceof String) {
1076 return syms.stringType;
1077 } else if (arg instanceof Pool.MethodHandle) {
1078 return syms.methodHandleType;
1079 } else if (arg instanceof MethodType) {
1080 return syms.methodTypeType;
1081 } else {
1082 Assert.error("bad static arg " + arg.getClass());
1083 return null;
1084 }
1085 }
1087 /**
1088 * Get the opcode associated with this method reference
1089 */
1090 private int referenceKind(Symbol refSym) {
1091 if (refSym.isConstructor()) {
1092 return ClassFile.REF_newInvokeSpecial;
1093 } else {
1094 if (refSym.isStatic()) {
1095 return ClassFile.REF_invokeStatic;
1096 } else if ((refSym.flags() & PRIVATE) != 0) {
1097 return ClassFile.REF_invokeSpecial;
1098 } else if (refSym.enclClass().isInterface()) {
1099 return ClassFile.REF_invokeInterface;
1100 } else {
1101 return ClassFile.REF_invokeVirtual;
1102 }
1103 }
1104 }
1106 // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer">
1107 /**
1108 * This visitor collects information about translation of a lambda expression.
1109 * More specifically, it keeps track of the enclosing contexts and captured locals
1110 * accessed by the lambda being translated (as well as other useful info).
1111 * It also translates away problems for LambdaToMethod.
1112 */
1113 class LambdaAnalyzerPreprocessor extends TreeTranslator {
1115 /** the frame stack - used to reconstruct translation info about enclosing scopes */
1116 private List<Frame> frameStack;
1118 /**
1119 * keep the count of lambda expression (used to generate unambiguous
1120 * names)
1121 */
1122 private int lambdaCount = 0;
1124 /**
1125 * keep the count of lambda expression defined in given context (used to
1126 * generate unambiguous names for serializable lambdas)
1127 */
1128 private class SyntheticMethodNameCounter {
1129 private Map<String, Integer> map = new HashMap<>();
1130 int getIndex(StringBuilder buf) {
1131 String temp = buf.toString();
1132 Integer count = map.get(temp);
1133 if (count == null) {
1134 count = 0;
1135 }
1136 ++count;
1137 map.put(temp, count);
1138 return count;
1139 }
1140 }
1141 private SyntheticMethodNameCounter syntheticMethodNameCounts =
1142 new SyntheticMethodNameCounter();
1144 private Map<Symbol, JCClassDecl> localClassDefs;
1146 /**
1147 * maps for fake clinit symbols to be used as owners of lambda occurring in
1148 * a static var init context
1149 */
1150 private Map<ClassSymbol, Symbol> clinits =
1151 new HashMap<ClassSymbol, Symbol>();
1153 private JCClassDecl analyzeAndPreprocessClass(JCClassDecl tree) {
1154 frameStack = List.nil();
1155 localClassDefs = new HashMap<Symbol, JCClassDecl>();
1156 return translate(tree);
1157 }
1159 @Override
1160 public void visitBlock(JCBlock tree) {
1161 List<Frame> prevStack = frameStack;
1162 try {
1163 if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) {
1164 frameStack = frameStack.prepend(new Frame(tree));
1165 }
1166 super.visitBlock(tree);
1167 }
1168 finally {
1169 frameStack = prevStack;
1170 }
1171 }
1173 @Override
1174 public void visitClassDef(JCClassDecl tree) {
1175 List<Frame> prevStack = frameStack;
1176 SyntheticMethodNameCounter prevSyntheticMethodNameCounts =
1177 syntheticMethodNameCounts;
1178 Map<ClassSymbol, Symbol> prevClinits = clinits;
1179 DiagnosticSource prevSource = log.currentSource();
1180 try {
1181 log.useSource(tree.sym.sourcefile);
1182 syntheticMethodNameCounts = new SyntheticMethodNameCounter();
1183 prevClinits = new HashMap<ClassSymbol, Symbol>();
1184 if (tree.sym.owner.kind == MTH) {
1185 localClassDefs.put(tree.sym, tree);
1186 }
1187 if (directlyEnclosingLambda() != null) {
1188 tree.sym.owner = owner();
1189 if (tree.sym.hasOuterInstance()) {
1190 //if a class is defined within a lambda, the lambda must capture
1191 //its enclosing instance (if any)
1192 TranslationContext<?> localContext = context();
1193 while (localContext != null) {
1194 if (localContext.tree.getTag() == LAMBDA) {
1195 ((LambdaTranslationContext)localContext)
1196 .addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS);
1197 }
1198 localContext = localContext.prev;
1199 }
1200 }
1201 }
1202 frameStack = frameStack.prepend(new Frame(tree));
1203 super.visitClassDef(tree);
1204 }
1205 finally {
1206 log.useSource(prevSource.getFile());
1207 frameStack = prevStack;
1208 syntheticMethodNameCounts = prevSyntheticMethodNameCounts;
1209 clinits = prevClinits;
1210 }
1211 }
1213 @Override
1214 public void visitIdent(JCIdent tree) {
1215 if (context() != null && lambdaIdentSymbolFilter(tree.sym)) {
1216 if (tree.sym.kind == VAR &&
1217 tree.sym.owner.kind == MTH &&
1218 tree.type.constValue() == null) {
1219 TranslationContext<?> localContext = context();
1220 while (localContext != null) {
1221 if (localContext.tree.getTag() == LAMBDA) {
1222 JCTree block = capturedDecl(localContext.depth, tree.sym);
1223 if (block == null) break;
1224 ((LambdaTranslationContext)localContext)
1225 .addSymbol(tree.sym, CAPTURED_VAR);
1226 }
1227 localContext = localContext.prev;
1228 }
1229 } else if (tree.sym.owner.kind == TYP) {
1230 TranslationContext<?> localContext = context();
1231 while (localContext != null) {
1232 if (localContext.tree.hasTag(LAMBDA)) {
1233 JCTree block = capturedDecl(localContext.depth, tree.sym);
1234 if (block == null) break;
1235 switch (block.getTag()) {
1236 case CLASSDEF:
1237 JCClassDecl cdecl = (JCClassDecl)block;
1238 ((LambdaTranslationContext)localContext)
1239 .addSymbol(cdecl.sym, CAPTURED_THIS);
1240 break;
1241 default:
1242 Assert.error("bad block kind");
1243 }
1244 }
1245 localContext = localContext.prev;
1246 }
1247 }
1248 }
1249 super.visitIdent(tree);
1250 }
1252 @Override
1253 public void visitLambda(JCLambda tree) {
1254 List<Frame> prevStack = frameStack;
1255 try {
1256 LambdaTranslationContext context = (LambdaTranslationContext)makeLambdaContext(tree);
1257 frameStack = frameStack.prepend(new Frame(tree));
1258 for (JCVariableDecl param : tree.params) {
1259 context.addSymbol(param.sym, PARAM);
1260 frameStack.head.addLocal(param.sym);
1261 }
1262 contextMap.put(tree, context);
1263 super.visitLambda(tree);
1264 context.complete();
1265 }
1266 finally {
1267 frameStack = prevStack;
1268 }
1269 }
1271 @Override
1272 public void visitMethodDef(JCMethodDecl tree) {
1273 List<Frame> prevStack = frameStack;
1274 try {
1275 frameStack = frameStack.prepend(new Frame(tree));
1276 super.visitMethodDef(tree);
1277 }
1278 finally {
1279 frameStack = prevStack;
1280 }
1281 }
1283 @Override
1284 public void visitNewClass(JCNewClass tree) {
1285 if (lambdaNewClassFilter(context(), tree)) {
1286 TranslationContext<?> localContext = context();
1287 while (localContext != null) {
1288 if (localContext.tree.getTag() == LAMBDA) {
1289 ((LambdaTranslationContext)localContext)
1290 .addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS);
1291 }
1292 localContext = localContext.prev;
1293 }
1294 }
1295 if (context() != null && tree.type.tsym.owner.kind == MTH) {
1296 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context();
1297 captureLocalClassDefs(tree.type.tsym, lambdaContext);
1298 }
1299 super.visitNewClass(tree);
1300 }
1301 //where
1302 void captureLocalClassDefs(Symbol csym, final LambdaTranslationContext lambdaContext) {
1303 JCClassDecl localCDef = localClassDefs.get(csym);
1304 if (localCDef != null && localCDef.pos < lambdaContext.tree.pos) {
1305 BasicFreeVarCollector fvc = lower.new BasicFreeVarCollector() {
1306 @Override
1307 void addFreeVars(ClassSymbol c) {
1308 captureLocalClassDefs(c, lambdaContext);
1309 }
1310 @Override
1311 void visitSymbol(Symbol sym) {
1312 if (sym.kind == VAR &&
1313 sym.owner.kind == MTH &&
1314 ((VarSymbol)sym).getConstValue() == null) {
1315 TranslationContext<?> localContext = context();
1316 while (localContext != null) {
1317 if (localContext.tree.getTag() == LAMBDA) {
1318 JCTree block = capturedDecl(localContext.depth, sym);
1319 if (block == null) break;
1320 ((LambdaTranslationContext)localContext).addSymbol(sym, CAPTURED_VAR);
1321 }
1322 localContext = localContext.prev;
1323 }
1324 }
1325 }
1326 };
1327 fvc.scan(localCDef);
1328 }
1329 }
1331 /**
1332 * Method references to local class constructors, may, if the local
1333 * class references local variables, have implicit constructor
1334 * parameters added in Lower; As a result, the invokedynamic bootstrap
1335 * information added in the LambdaToMethod pass will have the wrong
1336 * signature. Hooks between Lower and LambdaToMethod have been added to
1337 * handle normal "new" in this case. This visitor converts potentially
1338 * effected method references into a lambda containing a normal "new" of
1339 * the class.
1340 *
1341 * @param tree
1342 */
1343 @Override
1344 public void visitReference(JCMemberReference tree) {
1345 if (tree.getMode() == ReferenceMode.NEW
1346 && tree.kind != ReferenceKind.ARRAY_CTOR
1347 && tree.sym.owner.isLocal()) {
1348 MethodSymbol consSym = (MethodSymbol) tree.sym;
1349 List<Type> ptypes = ((MethodType) consSym.type).getParameterTypes();
1350 Type classType = consSym.owner.type;
1352 // Build lambda parameters
1353 // partially cloned from TreeMaker.Params until 8014021 is fixed
1354 Symbol owner = owner();
1355 ListBuffer<JCVariableDecl> paramBuff = new ListBuffer<JCVariableDecl>();
1356 int i = 0;
1357 for (List<Type> l = ptypes; l.nonEmpty(); l = l.tail) {
1358 JCVariableDecl param = make.Param(make.paramName(i++), l.head, owner);
1359 param.sym.pos = tree.pos;
1360 paramBuff.append(param);
1361 }
1362 List<JCVariableDecl> params = paramBuff.toList();
1364 // Make new-class call
1365 JCNewClass nc = makeNewClass(classType, make.Idents(params));
1366 nc.pos = tree.pos;
1368 // Make lambda holding the new-class call
1369 JCLambda slam = make.Lambda(params, nc);
1370 slam.targets = tree.targets;
1371 slam.type = tree.type;
1372 slam.pos = tree.pos;
1374 // Now it is a lambda, process as such
1375 visitLambda(slam);
1376 } else {
1377 super.visitReference(tree);
1378 contextMap.put(tree, makeReferenceContext(tree));
1379 }
1380 }
1382 @Override
1383 public void visitSelect(JCFieldAccess tree) {
1384 if (context() != null && tree.sym.kind == VAR &&
1385 (tree.sym.name == names._this ||
1386 tree.sym.name == names._super)) {
1387 // A select of this or super means, if we are in a lambda,
1388 // we much have an instance context
1389 TranslationContext<?> localContext = context();
1390 while (localContext != null) {
1391 if (localContext.tree.hasTag(LAMBDA)) {
1392 JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym);
1393 if (clazz == null) break;
1394 ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS);
1395 }
1396 localContext = localContext.prev;
1397 }
1398 }
1399 super.visitSelect(tree);
1400 }
1402 @Override
1403 public void visitVarDef(JCVariableDecl tree) {
1404 TranslationContext<?> context = context();
1405 LambdaTranslationContext ltc = (context != null && context instanceof LambdaTranslationContext)?
1406 (LambdaTranslationContext)context :
1407 null;
1408 if (ltc != null) {
1409 if (frameStack.head.tree.hasTag(LAMBDA)) {
1410 ltc.addSymbol(tree.sym, LOCAL_VAR);
1411 }
1412 // Check for type variables (including as type arguments).
1413 // If they occur within class nested in a lambda, mark for erasure
1414 Type type = tree.sym.asType();
1415 if (inClassWithinLambda() && !types.isSameType(types.erasure(type), type)) {
1416 ltc.addSymbol(tree.sym, TYPE_VAR);
1417 }
1418 }
1420 List<Frame> prevStack = frameStack;
1421 try {
1422 if (tree.sym.owner.kind == MTH) {
1423 frameStack.head.addLocal(tree.sym);
1424 }
1425 frameStack = frameStack.prepend(new Frame(tree));
1426 super.visitVarDef(tree);
1427 }
1428 finally {
1429 frameStack = prevStack;
1430 }
1431 }
1433 /**
1434 * Return a valid owner given the current declaration stack
1435 * (required to skip synthetic lambda symbols)
1436 */
1437 private Symbol owner() {
1438 return owner(false);
1439 }
1441 @SuppressWarnings("fallthrough")
1442 private Symbol owner(boolean skipLambda) {
1443 List<Frame> frameStack2 = frameStack;
1444 while (frameStack2.nonEmpty()) {
1445 switch (frameStack2.head.tree.getTag()) {
1446 case VARDEF:
1447 if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) {
1448 frameStack2 = frameStack2.tail;
1449 break;
1450 }
1451 JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree;
1452 return initSym(cdecl.sym,
1453 ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC);
1454 case BLOCK:
1455 JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree;
1456 return initSym(cdecl2.sym,
1457 ((JCBlock)frameStack2.head.tree).flags & STATIC);
1458 case CLASSDEF:
1459 return ((JCClassDecl)frameStack2.head.tree).sym;
1460 case METHODDEF:
1461 return ((JCMethodDecl)frameStack2.head.tree).sym;
1462 case LAMBDA:
1463 if (!skipLambda)
1464 return ((LambdaTranslationContext)contextMap
1465 .get(frameStack2.head.tree)).translatedSym;
1466 default:
1467 frameStack2 = frameStack2.tail;
1468 }
1469 }
1470 Assert.error();
1471 return null;
1472 }
1474 private Symbol initSym(ClassSymbol csym, long flags) {
1475 boolean isStatic = (flags & STATIC) != 0;
1476 if (isStatic) {
1477 /* static clinits are generated in Gen, so we need to use a fake
1478 * one. Attr creates a fake clinit method while attributing
1479 * lambda expressions used as initializers of static fields, so
1480 * let's use that one.
1481 */
1482 MethodSymbol clinit = attr.removeClinit(csym);
1483 if (clinit != null) {
1484 clinits.put(csym, clinit);
1485 return clinit;
1486 }
1488 /* if no clinit is found at Attr, then let's try at clinits.
1489 */
1490 clinit = (MethodSymbol)clinits.get(csym);
1491 if (clinit == null) {
1492 /* no luck, let's create a new one
1493 */
1494 clinit = makePrivateSyntheticMethod(STATIC,
1495 names.clinit,
1496 new MethodType(List.<Type>nil(), syms.voidType,
1497 List.<Type>nil(), syms.methodClass),
1498 csym);
1499 clinits.put(csym, clinit);
1500 }
1501 return clinit;
1502 } else {
1503 //get the first constructor and treat it as the instance init sym
1504 for (Symbol s : csym.members_field.getElementsByName(names.init)) {
1505 return s;
1506 }
1507 }
1508 Assert.error("init not found");
1509 return null;
1510 }
1512 private JCTree directlyEnclosingLambda() {
1513 if (frameStack.isEmpty()) {
1514 return null;
1515 }
1516 List<Frame> frameStack2 = frameStack;
1517 while (frameStack2.nonEmpty()) {
1518 switch (frameStack2.head.tree.getTag()) {
1519 case CLASSDEF:
1520 case METHODDEF:
1521 return null;
1522 case LAMBDA:
1523 return frameStack2.head.tree;
1524 default:
1525 frameStack2 = frameStack2.tail;
1526 }
1527 }
1528 Assert.error();
1529 return null;
1530 }
1532 private boolean inClassWithinLambda() {
1533 if (frameStack.isEmpty()) {
1534 return false;
1535 }
1536 List<Frame> frameStack2 = frameStack;
1537 boolean classFound = false;
1538 while (frameStack2.nonEmpty()) {
1539 switch (frameStack2.head.tree.getTag()) {
1540 case LAMBDA:
1541 return classFound;
1542 case CLASSDEF:
1543 classFound = true;
1544 frameStack2 = frameStack2.tail;
1545 break;
1546 default:
1547 frameStack2 = frameStack2.tail;
1548 }
1549 }
1550 // No lambda
1551 return false;
1552 }
1554 /**
1555 * Return the declaration corresponding to a symbol in the enclosing
1556 * scope; the depth parameter is used to filter out symbols defined
1557 * in nested scopes (which do not need to undergo capture).
1558 */
1559 private JCTree capturedDecl(int depth, Symbol sym) {
1560 int currentDepth = frameStack.size() - 1;
1561 for (Frame block : frameStack) {
1562 switch (block.tree.getTag()) {
1563 case CLASSDEF:
1564 ClassSymbol clazz = ((JCClassDecl)block.tree).sym;
1565 if (sym.isMemberOf(clazz, types)) {
1566 return currentDepth > depth ? null : block.tree;
1567 }
1568 break;
1569 case VARDEF:
1570 if (((JCVariableDecl)block.tree).sym == sym &&
1571 sym.owner.kind == MTH) { //only locals are captured
1572 return currentDepth > depth ? null : block.tree;
1573 }
1574 break;
1575 case BLOCK:
1576 case METHODDEF:
1577 case LAMBDA:
1578 if (block.locals != null && block.locals.contains(sym)) {
1579 return currentDepth > depth ? null : block.tree;
1580 }
1581 break;
1582 default:
1583 Assert.error("bad decl kind " + block.tree.getTag());
1584 }
1585 currentDepth--;
1586 }
1587 return null;
1588 }
1590 private TranslationContext<?> context() {
1591 for (Frame frame : frameStack) {
1592 TranslationContext<?> context = contextMap.get(frame.tree);
1593 if (context != null) {
1594 return context;
1595 }
1596 }
1597 return null;
1598 }
1600 /**
1601 * This is used to filter out those identifiers that needs to be adjusted
1602 * when translating away lambda expressions
1603 */
1604 private boolean lambdaIdentSymbolFilter(Symbol sym) {
1605 return (sym.kind == VAR || sym.kind == MTH)
1606 && !sym.isStatic()
1607 && sym.name != names.init;
1608 }
1610 /**
1611 * This is used to filter out those new class expressions that need to
1612 * be qualified with an enclosing tree
1613 */
1614 private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) {
1615 if (context != null
1616 && tree.encl == null
1617 && tree.def == null
1618 && !tree.type.getEnclosingType().hasTag(NONE)) {
1619 Type encl = tree.type.getEnclosingType();
1620 Type current = context.owner.enclClass().type;
1621 while (!current.hasTag(NONE)) {
1622 if (current.tsym.isSubClass(encl.tsym, types)) {
1623 return true;
1624 }
1625 current = current.getEnclosingType();
1626 }
1627 return false;
1628 } else {
1629 return false;
1630 }
1631 }
1633 private TranslationContext<JCLambda> makeLambdaContext(JCLambda tree) {
1634 return new LambdaTranslationContext(tree);
1635 }
1637 private TranslationContext<JCMemberReference> makeReferenceContext(JCMemberReference tree) {
1638 return new ReferenceTranslationContext(tree);
1639 }
1641 private class Frame {
1642 final JCTree tree;
1643 List<Symbol> locals;
1645 public Frame(JCTree tree) {
1646 this.tree = tree;
1647 }
1649 void addLocal(Symbol sym) {
1650 if (locals == null) {
1651 locals = List.nil();
1652 }
1653 locals = locals.prepend(sym);
1654 }
1655 }
1657 /**
1658 * This class is used to store important information regarding translation of
1659 * lambda expression/method references (see subclasses).
1660 */
1661 private abstract class TranslationContext<T extends JCFunctionalExpression> {
1663 /** the underlying (untranslated) tree */
1664 final T tree;
1666 /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */
1667 final Symbol owner;
1669 /** the depth of this lambda expression in the frame stack */
1670 final int depth;
1672 /** the enclosing translation context (set for nested lambdas/mref) */
1673 final TranslationContext<?> prev;
1675 /** list of methods to be bridged by the meta-factory */
1676 final List<Symbol> bridges;
1678 TranslationContext(T tree) {
1679 this.tree = tree;
1680 this.owner = owner();
1681 this.depth = frameStack.size() - 1;
1682 this.prev = context();
1683 ClassSymbol csym =
1684 types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.targets, ABSTRACT | INTERFACE);
1685 this.bridges = types.functionalInterfaceBridges(csym);
1686 }
1688 /** does this functional expression need to be created using alternate metafactory? */
1689 boolean needsAltMetafactory() {
1690 return tree.targets.length() > 1 ||
1691 isSerializable() ||
1692 bridges.length() > 1;
1693 }
1695 /** does this functional expression require serialization support? */
1696 boolean isSerializable() {
1697 if (forceSerializable) {
1698 return true;
1699 }
1700 for (Type target : tree.targets) {
1701 if (types.asSuper(target, syms.serializableType.tsym) != null) {
1702 return true;
1703 }
1704 }
1705 return false;
1706 }
1708 /**
1709 * @return Name of the enclosing method to be folded into synthetic
1710 * method name
1711 */
1712 String enclosingMethodName() {
1713 return syntheticMethodNameComponent(owner.name);
1714 }
1716 /**
1717 * @return Method name in a form that can be folded into a
1718 * component of a synthetic method name
1719 */
1720 String syntheticMethodNameComponent(Name name) {
1721 if (name == null) {
1722 return "null";
1723 }
1724 String methodName = name.toString();
1725 if (methodName.equals("<clinit>")) {
1726 methodName = "static";
1727 } else if (methodName.equals("<init>")) {
1728 methodName = "new";
1729 }
1730 return methodName;
1731 }
1732 }
1734 /**
1735 * This class retains all the useful information about a lambda expression;
1736 * the contents of this class are filled by the LambdaAnalyzer visitor,
1737 * and the used by the main translation routines in order to adjust references
1738 * to captured locals/members, etc.
1739 */
1740 private class LambdaTranslationContext extends TranslationContext<JCLambda> {
1742 /** variable in the enclosing context to which this lambda is assigned */
1743 final Symbol self;
1745 /** variable in the enclosing context to which this lambda is assigned */
1746 final Symbol assignedTo;
1748 Map<LambdaSymbolKind, Map<Symbol, Symbol>> translatedSymbols;
1750 /** the synthetic symbol for the method hoisting the translated lambda */
1751 Symbol translatedSym;
1753 List<JCVariableDecl> syntheticParams;
1755 LambdaTranslationContext(JCLambda tree) {
1756 super(tree);
1757 Frame frame = frameStack.head;
1758 switch (frame.tree.getTag()) {
1759 case VARDEF:
1760 assignedTo = self = ((JCVariableDecl) frame.tree).sym;
1761 break;
1762 case ASSIGN:
1763 self = null;
1764 assignedTo = TreeInfo.symbol(((JCAssign) frame.tree).getVariable());
1765 break;
1766 default:
1767 assignedTo = self = null;
1768 break;
1769 }
1771 // This symbol will be filled-in in complete
1772 this.translatedSym = makePrivateSyntheticMethod(0, null, null, owner.enclClass());
1774 if (dumpLambdaToMethodStats) {
1775 log.note(tree, "lambda.stat", needsAltMetafactory(), translatedSym);
1776 }
1777 translatedSymbols = new EnumMap<>(LambdaSymbolKind.class);
1779 translatedSymbols.put(PARAM, new LinkedHashMap<Symbol, Symbol>());
1780 translatedSymbols.put(LOCAL_VAR, new LinkedHashMap<Symbol, Symbol>());
1781 translatedSymbols.put(CAPTURED_VAR, new LinkedHashMap<Symbol, Symbol>());
1782 translatedSymbols.put(CAPTURED_THIS, new LinkedHashMap<Symbol, Symbol>());
1783 translatedSymbols.put(TYPE_VAR, new LinkedHashMap<Symbol, Symbol>());
1784 }
1786 /**
1787 * For a serializable lambda, generate a disambiguating string
1788 * which maximizes stability across deserialization.
1789 *
1790 * @return String to differentiate synthetic lambda method names
1791 */
1792 private String serializedLambdaDisambiguation() {
1793 StringBuilder buf = new StringBuilder();
1794 // Append the enclosing method signature to differentiate
1795 // overloaded enclosing methods. For lambdas enclosed in
1796 // lambdas, the generated lambda method will not have type yet,
1797 // but the enclosing method's name will have been generated
1798 // with this same method, so it will be unique and never be
1799 // overloaded.
1800 Assert.check(
1801 owner.type != null ||
1802 directlyEnclosingLambda() != null);
1803 if (owner.type != null) {
1804 buf.append(typeSig(owner.type));
1805 buf.append(":");
1806 }
1808 // Add target type info
1809 buf.append(types.findDescriptorSymbol(tree.type.tsym).owner.flatName());
1810 buf.append(" ");
1812 // Add variable assigned to
1813 if (assignedTo != null) {
1814 buf.append(assignedTo.flatName());
1815 buf.append("=");
1816 }
1817 //add captured locals info: type, name, order
1818 for (Symbol fv : getSymbolMap(CAPTURED_VAR).keySet()) {
1819 if (fv != self) {
1820 buf.append(typeSig(fv.type));
1821 buf.append(" ");
1822 buf.append(fv.flatName());
1823 buf.append(",");
1824 }
1825 }
1827 return buf.toString();
1828 }
1830 /**
1831 * For a non-serializable lambda, generate a simple method.
1832 *
1833 * @return Name to use for the synthetic lambda method name
1834 */
1835 private Name lambdaName() {
1836 return names.lambda.append(names.fromString(enclosingMethodName() + "$" + lambdaCount++));
1837 }
1839 /**
1840 * For a serializable lambda, generate a method name which maximizes
1841 * name stability across deserialization.
1842 *
1843 * @return Name to use for the synthetic lambda method name
1844 */
1845 private Name serializedLambdaName() {
1846 StringBuilder buf = new StringBuilder();
1847 buf.append(names.lambda);
1848 // Append the name of the method enclosing the lambda.
1849 buf.append(enclosingMethodName());
1850 buf.append('$');
1851 // Append a hash of the disambiguating string : enclosing method
1852 // signature, etc.
1853 String disam = serializedLambdaDisambiguation();
1854 buf.append(Integer.toHexString(disam.hashCode()));
1855 buf.append('$');
1856 // The above appended name components may not be unique, append
1857 // a count based on the above name components.
1858 buf.append(syntheticMethodNameCounts.getIndex(buf));
1859 String result = buf.toString();
1860 //System.err.printf("serializedLambdaName: %s -- %s\n", result, disam);
1861 return names.fromString(result);
1862 }
1864 /**
1865 * Translate a symbol of a given kind into something suitable for the
1866 * synthetic lambda body
1867 */
1868 Symbol translate(Name name, final Symbol sym, LambdaSymbolKind skind) {
1869 Symbol ret;
1870 switch (skind) {
1871 case CAPTURED_THIS:
1872 ret = sym; // self represented
1873 break;
1874 case TYPE_VAR:
1875 // Just erase the type var
1876 ret = new VarSymbol(sym.flags(), name,
1877 types.erasure(sym.type), sym.owner);
1879 /* this information should also be kept for LVT generation at Gen
1880 * a Symbol with pos < startPos won't be tracked.
1881 */
1882 ((VarSymbol)ret).pos = ((VarSymbol)sym).pos;
1883 break;
1884 case CAPTURED_VAR:
1885 ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym) {
1886 @Override
1887 public Symbol baseSymbol() {
1888 //keep mapping with original captured symbol
1889 return sym;
1890 }
1891 };
1892 break;
1893 case LOCAL_VAR:
1894 ret = new VarSymbol(sym.flags() & FINAL, name, sym.type, translatedSym);
1895 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
1896 break;
1897 case PARAM:
1898 ret = new VarSymbol((sym.flags() & FINAL) | PARAMETER, name, types.erasure(sym.type), translatedSym);
1899 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
1900 break;
1901 default:
1902 ret = makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
1903 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
1904 }
1905 if (ret != sym) {
1906 ret.setDeclarationAttributes(sym.getRawAttributes());
1907 ret.setTypeAttributes(sym.getRawTypeAttributes());
1908 }
1909 return ret;
1910 }
1912 void addSymbol(Symbol sym, LambdaSymbolKind skind) {
1913 Map<Symbol, Symbol> transMap = getSymbolMap(skind);
1914 Name preferredName;
1915 switch (skind) {
1916 case CAPTURED_THIS:
1917 preferredName = names.fromString("encl$" + transMap.size());
1918 break;
1919 case CAPTURED_VAR:
1920 preferredName = names.fromString("cap$" + transMap.size());
1921 break;
1922 case LOCAL_VAR:
1923 preferredName = sym.name;
1924 break;
1925 case PARAM:
1926 preferredName = sym.name;
1927 break;
1928 case TYPE_VAR:
1929 preferredName = sym.name;
1930 break;
1931 default: throw new AssertionError();
1932 }
1933 if (!transMap.containsKey(sym)) {
1934 transMap.put(sym, translate(preferredName, sym, skind));
1935 }
1936 }
1938 Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind skind) {
1939 Map<Symbol, Symbol> m = translatedSymbols.get(skind);
1940 Assert.checkNonNull(m);
1941 return m;
1942 }
1944 JCTree translate(JCIdent lambdaIdent) {
1945 for (Map<Symbol, Symbol> m : translatedSymbols.values()) {
1946 if (m.containsKey(lambdaIdent.sym)) {
1947 Symbol tSym = m.get(lambdaIdent.sym);
1948 JCTree t = make.Ident(tSym).setType(lambdaIdent.type);
1949 tSym.setTypeAttributes(lambdaIdent.sym.getRawTypeAttributes());
1950 return t;
1951 }
1952 }
1953 return null;
1954 }
1956 /**
1957 * The translatedSym is not complete/accurate until the analysis is
1958 * finished. Once the analysis is finished, the translatedSym is
1959 * "completed" -- updated with type information, access modifiers,
1960 * and full parameter list.
1961 */
1962 void complete() {
1963 if (syntheticParams != null) {
1964 return;
1965 }
1966 boolean inInterface = translatedSym.owner.isInterface();
1967 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty();
1969 // If instance access isn't needed, make it static.
1970 // Interface instance methods must be default methods.
1971 // Lambda methods are private synthetic.
1972 translatedSym.flags_field = SYNTHETIC | LAMBDA_METHOD |
1973 PRIVATE |
1974 (thisReferenced? (inInterface? DEFAULT : 0) : STATIC);
1976 //compute synthetic params
1977 ListBuffer<JCVariableDecl> params = new ListBuffer<>();
1979 // The signature of the method is augmented with the following
1980 // synthetic parameters:
1981 //
1982 // 1) reference to enclosing contexts captured by the lambda expression
1983 // 2) enclosing locals captured by the lambda expression
1984 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR).values()) {
1985 params.append(make.VarDef((VarSymbol) thisSym, null));
1986 }
1987 for (Symbol thisSym : getSymbolMap(PARAM).values()) {
1988 params.append(make.VarDef((VarSymbol) thisSym, null));
1989 }
1990 syntheticParams = params.toList();
1992 // Compute and set the lambda name
1993 translatedSym.name = isSerializable()
1994 ? serializedLambdaName()
1995 : lambdaName();
1997 //prepend synthetic args to translated lambda method signature
1998 translatedSym.type = types.createMethodTypeWithParameters(
1999 generatedLambdaSig(),
2000 TreeInfo.types(syntheticParams));
2001 }
2003 Type generatedLambdaSig() {
2004 return types.erasure(tree.getDescriptorType(types));
2005 }
2006 }
2008 /**
2009 * This class retains all the useful information about a method reference;
2010 * the contents of this class are filled by the LambdaAnalyzer visitor,
2011 * and the used by the main translation routines in order to adjust method
2012 * references (i.e. in case a bridge is needed)
2013 */
2014 private class ReferenceTranslationContext extends TranslationContext<JCMemberReference> {
2016 final boolean isSuper;
2017 final Symbol bridgeSym;
2018 final Symbol sigPolySym;
2020 ReferenceTranslationContext(JCMemberReference tree) {
2021 super(tree);
2022 this.isSuper = tree.hasKind(ReferenceKind.SUPER);
2023 this.bridgeSym = needsBridge()
2024 ? makePrivateSyntheticMethod(isSuper ? 0 : STATIC,
2025 referenceBridgeName(), null,
2026 owner.enclClass())
2027 : null;
2028 this.sigPolySym = isSignaturePolymorphic()
2029 ? makePrivateSyntheticMethod(tree.sym.flags(),
2030 tree.sym.name,
2031 bridgedRefSig(),
2032 tree.sym.enclClass())
2033 : null;
2034 if (dumpLambdaToMethodStats) {
2035 String key = bridgeSym == null ?
2036 "mref.stat" : "mref.stat.1";
2037 log.note(tree, key, needsAltMetafactory(), bridgeSym);
2038 }
2039 }
2041 /**
2042 * Get the opcode associated with this method reference
2043 */
2044 int referenceKind() {
2045 return LambdaToMethod.this.referenceKind(needsBridge()
2046 ? bridgeSym
2047 : tree.sym);
2048 }
2050 boolean needsVarArgsConversion() {
2051 return tree.varargsElement != null;
2052 }
2054 /**
2055 * Generate a disambiguating string to increase stability (important
2056 * if serialized)
2057 *
2058 * @return String to differentiate synthetic lambda method names
2059 */
2060 private String referenceBridgeDisambiguation() {
2061 StringBuilder buf = new StringBuilder();
2062 // Append the enclosing method signature to differentiate
2063 // overloaded enclosing methods.
2064 if (owner.type != null) {
2065 buf.append(typeSig(owner.type));
2066 buf.append(":");
2067 }
2069 // Append qualifier type
2070 buf.append(classSig(tree.sym.owner.type));
2072 // Note static/instance
2073 buf.append(tree.sym.isStatic()? " S " : " I ");
2075 // Append referenced signature
2076 buf.append(typeSig(tree.sym.erasure(types)));
2078 return buf.toString();
2079 }
2081 /**
2082 * Construct a unique stable name for the method reference bridge
2083 *
2084 * @return Name to use for the synthetic method name
2085 */
2086 private Name referenceBridgeName() {
2087 StringBuilder buf = new StringBuilder();
2088 // Append lambda ID, this is semantically significant
2089 buf.append(names.lambda);
2090 // Note that it is a method reference bridge
2091 buf.append("MR$");
2092 // Append the enclosing method name
2093 buf.append(enclosingMethodName());
2094 buf.append('$');
2095 // Append the referenced method name
2096 buf.append(syntheticMethodNameComponent(tree.sym.name));
2097 buf.append('$');
2098 // Append a hash of the disambiguating string : enclosing method
2099 // signature, etc.
2100 String disam = referenceBridgeDisambiguation();
2101 buf.append(Integer.toHexString(disam.hashCode()));
2102 buf.append('$');
2103 // The above appended name components may not be unique, append
2104 // a count based on the above name components.
2105 buf.append(syntheticMethodNameCounts.getIndex(buf));
2106 String result = buf.toString();
2107 return names.fromString(result);
2108 }
2110 /**
2111 * @return Is this an array operation like clone()
2112 */
2113 boolean isArrayOp() {
2114 return tree.sym.owner == syms.arrayClass;
2115 }
2117 boolean receiverAccessible() {
2118 //hack needed to workaround 292 bug (7087658)
2119 //when 292 issue is fixed we should remove this and change the backend
2120 //code to always generate a method handle to an accessible method
2121 return tree.ownerAccessible;
2122 }
2124 /**
2125 * The VM does not support access across nested classes (8010319).
2126 * Were that ever to change, this should be removed.
2127 */
2128 boolean isPrivateInOtherClass() {
2129 return (tree.sym.flags() & PRIVATE) != 0 &&
2130 !types.isSameType(
2131 types.erasure(tree.sym.enclClass().asType()),
2132 types.erasure(owner.enclClass().asType()));
2133 }
2135 /**
2136 * Signature polymorphic methods need special handling.
2137 * e.g. MethodHandle.invoke() MethodHandle.invokeExact()
2138 */
2139 final boolean isSignaturePolymorphic() {
2140 return tree.sym.kind == MTH &&
2141 types.isSignaturePolymorphic((MethodSymbol)tree.sym);
2142 }
2144 /**
2145 * Does this reference needs a bridge (i.e. var args need to be
2146 * expanded or "super" is used)
2147 */
2148 final boolean needsBridge() {
2149 return isSuper || needsVarArgsConversion() || isArrayOp() ||
2150 isPrivateInOtherClass() ||
2151 !receiverAccessible();
2152 }
2154 Type generatedRefSig() {
2155 return types.erasure(tree.sym.type);
2156 }
2158 Type bridgedRefSig() {
2159 return types.erasure(types.findDescriptorSymbol(tree.targets.head.tsym).type);
2160 }
2161 }
2162 }
2163 // </editor-fold>
2165 /*
2166 * These keys provide mappings for various translated lambda symbols
2167 * and the prevailing order must be maintained.
2168 */
2169 enum LambdaSymbolKind {
2170 PARAM, // original to translated lambda parameters
2171 LOCAL_VAR, // original to translated lambda locals
2172 CAPTURED_VAR, // variables in enclosing scope to translated synthetic parameters
2173 CAPTURED_THIS, // class symbols to translated synthetic parameters (for captured member access)
2174 TYPE_VAR; // original to translated lambda type variables
2175 }
2177 /**
2178 * ****************************************************************
2179 * Signature Generation
2180 * ****************************************************************
2181 */
2183 private String typeSig(Type type) {
2184 L2MSignatureGenerator sg = new L2MSignatureGenerator();
2185 sg.assembleSig(type);
2186 return sg.toString();
2187 }
2189 private String classSig(Type type) {
2190 L2MSignatureGenerator sg = new L2MSignatureGenerator();
2191 sg.assembleClassSig(type);
2192 return sg.toString();
2193 }
2195 /**
2196 * Signature Generation
2197 */
2198 private class L2MSignatureGenerator extends Types.SignatureGenerator {
2200 /**
2201 * An output buffer for type signatures.
2202 */
2203 StringBuilder sb = new StringBuilder();
2205 L2MSignatureGenerator() {
2206 super(types);
2207 }
2209 @Override
2210 protected void append(char ch) {
2211 sb.append(ch);
2212 }
2214 @Override
2215 protected void append(byte[] ba) {
2216 sb.append(new String(ba));
2217 }
2219 @Override
2220 protected void append(Name name) {
2221 sb.append(name.toString());
2222 }
2224 @Override
2225 public String toString() {
2226 return sb.toString();
2227 }
2228 }
2229 }