Tue, 25 Nov 2014 13:37:34 -0800
8058112: Invalid BootstrapMethod for constructor/method reference
Summary: Bridge method references with functional interface method parameters of intersection type
Reviewed-by: vromero, dlsmith
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.TypeSymbol;
40 import com.sun.tools.javac.code.Symbol.VarSymbol;
41 import com.sun.tools.javac.code.Symtab;
42 import com.sun.tools.javac.code.Type;
43 import com.sun.tools.javac.code.Type.MethodType;
44 import com.sun.tools.javac.code.Type.TypeVar;
45 import com.sun.tools.javac.code.Types;
46 import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzerPreprocessor.*;
47 import com.sun.tools.javac.comp.Lower.BasicFreeVarCollector;
48 import com.sun.tools.javac.jvm.*;
49 import com.sun.tools.javac.util.*;
50 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
51 import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
53 import java.util.EnumMap;
54 import java.util.HashMap;
55 import java.util.HashSet;
56 import java.util.LinkedHashMap;
57 import java.util.Map;
58 import java.util.Set;
60 import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*;
61 import static com.sun.tools.javac.code.Flags.*;
62 import static com.sun.tools.javac.code.Kinds.*;
63 import static com.sun.tools.javac.code.TypeTag.*;
64 import static com.sun.tools.javac.tree.JCTree.Tag.*;
65 import javax.lang.model.type.TypeKind;
67 /**
68 * This pass desugars lambda expressions into static methods
69 *
70 * <p><b>This is NOT part of any supported API.
71 * If you write code that depends on this, you do so at your own risk.
72 * This code and its internal interfaces are subject to change or
73 * deletion without notice.</b>
74 */
75 public class LambdaToMethod extends TreeTranslator {
77 private Attr attr;
78 private JCDiagnostic.Factory diags;
79 private Log log;
80 private Lower lower;
81 private Names names;
82 private Symtab syms;
83 private Resolve rs;
84 private TreeMaker make;
85 private Types types;
86 private TransTypes transTypes;
87 private Env<AttrContext> attrEnv;
89 /** the analyzer scanner */
90 private LambdaAnalyzerPreprocessor analyzer;
92 /** map from lambda trees to translation contexts */
93 private Map<JCTree, TranslationContext<?>> contextMap;
95 /** current translation context (visitor argument) */
96 private TranslationContext<?> context;
98 /** info about the current class being processed */
99 private KlassInfo kInfo;
101 /** dump statistics about lambda code generation */
102 private boolean dumpLambdaToMethodStats;
104 /** force serializable representation, for stress testing **/
105 private final boolean forceSerializable;
107 /** Flag for alternate metafactories indicating the lambda object is intended to be serializable */
108 public static final int FLAG_SERIALIZABLE = 1 << 0;
110 /** Flag for alternate metafactories indicating the lambda object has multiple targets */
111 public static final int FLAG_MARKERS = 1 << 1;
113 /** Flag for alternate metafactories indicating the lambda object requires multiple bridges */
114 public static final int FLAG_BRIDGES = 1 << 2;
116 // <editor-fold defaultstate="collapsed" desc="Instantiating">
117 protected static final Context.Key<LambdaToMethod> unlambdaKey =
118 new Context.Key<LambdaToMethod>();
120 public static LambdaToMethod instance(Context context) {
121 LambdaToMethod instance = context.get(unlambdaKey);
122 if (instance == null) {
123 instance = new LambdaToMethod(context);
124 }
125 return instance;
126 }
127 private LambdaToMethod(Context context) {
128 context.put(unlambdaKey, this);
129 diags = JCDiagnostic.Factory.instance(context);
130 log = Log.instance(context);
131 lower = Lower.instance(context);
132 names = Names.instance(context);
133 syms = Symtab.instance(context);
134 rs = Resolve.instance(context);
135 make = TreeMaker.instance(context);
136 types = Types.instance(context);
137 transTypes = TransTypes.instance(context);
138 analyzer = new LambdaAnalyzerPreprocessor();
139 Options options = Options.instance(context);
140 dumpLambdaToMethodStats = options.isSet("dumpLambdaToMethodStats");
141 attr = Attr.instance(context);
142 forceSerializable = options.isSet("forceSerializable");
143 }
144 // </editor-fold>
146 private class KlassInfo {
148 /**
149 * list of methods to append
150 */
151 private ListBuffer<JCTree> appendedMethodList;
153 /**
154 * list of deserialization cases
155 */
156 private final Map<String, ListBuffer<JCStatement>> deserializeCases;
158 /**
159 * deserialize method symbol
160 */
161 private final MethodSymbol deserMethodSym;
163 /**
164 * deserialize method parameter symbol
165 */
166 private final VarSymbol deserParamSym;
168 private final JCClassDecl clazz;
170 private KlassInfo(JCClassDecl clazz) {
171 this.clazz = clazz;
172 appendedMethodList = new ListBuffer<>();
173 deserializeCases = new HashMap<String, ListBuffer<JCStatement>>();
174 MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType,
175 List.<Type>nil(), syms.methodClass);
176 deserMethodSym = makePrivateSyntheticMethod(STATIC, names.deserializeLambda, type, clazz.sym);
177 deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"),
178 syms.serializedLambdaType, deserMethodSym);
179 }
181 private void addMethod(JCTree decl) {
182 appendedMethodList = appendedMethodList.prepend(decl);
183 }
184 }
186 // <editor-fold defaultstate="collapsed" desc="translate methods">
187 @Override
188 public <T extends JCTree> T translate(T tree) {
189 TranslationContext<?> newContext = contextMap.get(tree);
190 return translate(tree, newContext != null ? newContext : context);
191 }
193 <T extends JCTree> T translate(T tree, TranslationContext<?> newContext) {
194 TranslationContext<?> prevContext = context;
195 try {
196 context = newContext;
197 return super.translate(tree);
198 }
199 finally {
200 context = prevContext;
201 }
202 }
204 <T extends JCTree> List<T> translate(List<T> trees, TranslationContext<?> newContext) {
205 ListBuffer<T> buf = new ListBuffer<>();
206 for (T tree : trees) {
207 buf.append(translate(tree, newContext));
208 }
209 return buf.toList();
210 }
212 public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
213 this.make = make;
214 this.attrEnv = env;
215 this.context = null;
216 this.contextMap = new HashMap<JCTree, TranslationContext<?>>();
217 return translate(cdef);
218 }
219 // </editor-fold>
221 // <editor-fold defaultstate="collapsed" desc="visitor methods">
222 /**
223 * Visit a class.
224 * Maintain the translatedMethodList across nested classes.
225 * Append the translatedMethodList to the class after it is translated.
226 * @param tree
227 */
228 @Override
229 public void visitClassDef(JCClassDecl tree) {
230 if (tree.sym.owner.kind == PCK) {
231 //analyze class
232 tree = analyzer.analyzeAndPreprocessClass(tree);
233 }
234 KlassInfo prevKlassInfo = kInfo;
235 try {
236 kInfo = new KlassInfo(tree);
237 super.visitClassDef(tree);
238 if (!kInfo.deserializeCases.isEmpty()) {
239 int prevPos = make.pos;
240 try {
241 make.at(tree);
242 kInfo.addMethod(makeDeserializeMethod(tree.sym));
243 } finally {
244 make.at(prevPos);
245 }
246 }
247 //add all translated instance methods here
248 List<JCTree> newMethods = kInfo.appendedMethodList.toList();
249 tree.defs = tree.defs.appendList(newMethods);
250 for (JCTree lambda : newMethods) {
251 tree.sym.members().enter(((JCMethodDecl)lambda).sym);
252 }
253 result = tree;
254 } finally {
255 kInfo = prevKlassInfo;
256 }
257 }
259 /**
260 * Translate a lambda into a method to be inserted into the class.
261 * Then replace the lambda site with an invokedynamic call of to lambda
262 * meta-factory, which will use the lambda method.
263 * @param tree
264 */
265 @Override
266 public void visitLambda(JCLambda tree) {
267 LambdaTranslationContext localContext = (LambdaTranslationContext)context;
268 MethodSymbol sym = (MethodSymbol)localContext.translatedSym;
269 MethodType lambdaType = (MethodType) sym.type;
271 {
272 Symbol owner = localContext.owner;
273 ListBuffer<Attribute.TypeCompound> ownerTypeAnnos = new ListBuffer<Attribute.TypeCompound>();
274 ListBuffer<Attribute.TypeCompound> lambdaTypeAnnos = new ListBuffer<Attribute.TypeCompound>();
276 for (Attribute.TypeCompound tc : owner.getRawTypeAttributes()) {
277 if (tc.position.onLambda == tree) {
278 lambdaTypeAnnos.append(tc);
279 } else {
280 ownerTypeAnnos.append(tc);
281 }
282 }
283 if (lambdaTypeAnnos.nonEmpty()) {
284 owner.setTypeAttributes(ownerTypeAnnos.toList());
285 sym.setTypeAttributes(lambdaTypeAnnos.toList());
286 }
287 }
289 //create the method declaration hoisting the lambda body
290 JCMethodDecl lambdaDecl = make.MethodDef(make.Modifiers(sym.flags_field),
291 sym.name,
292 make.QualIdent(lambdaType.getReturnType().tsym),
293 List.<JCTypeParameter>nil(),
294 localContext.syntheticParams,
295 lambdaType.getThrownTypes() == null ?
296 List.<JCExpression>nil() :
297 make.Types(lambdaType.getThrownTypes()),
298 null,
299 null);
300 lambdaDecl.sym = sym;
301 lambdaDecl.type = lambdaType;
303 //translate lambda body
304 //As the lambda body is translated, all references to lambda locals,
305 //captured variables, enclosing members are adjusted accordingly
306 //to refer to the static method parameters (rather than i.e. acessing to
307 //captured members directly).
308 lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl));
310 //Add the method to the list of methods to be added to this class.
311 kInfo.addMethod(lambdaDecl);
313 //now that we have generated a method for the lambda expression,
314 //we can translate the lambda into a method reference pointing to the newly
315 //created method.
316 //
317 //Note that we need to adjust the method handle so that it will match the
318 //signature of the SAM descriptor - this means that the method reference
319 //should be added the following synthetic arguments:
320 //
321 // * the "this" argument if it is an instance method
322 // * enclosing locals captured by the lambda expression
324 ListBuffer<JCExpression> syntheticInits = new ListBuffer<>();
326 if (localContext.methodReferenceReceiver != null) {
327 syntheticInits.append(localContext.methodReferenceReceiver);
328 } else if (!sym.isStatic()) {
329 syntheticInits.append(makeThis(
330 sym.owner.enclClass().asType(),
331 localContext.owner.enclClass()));
332 }
334 //add captured locals
335 for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) {
336 if (fv != localContext.self) {
337 JCTree captured_local = make.Ident(fv).setType(fv.type);
338 syntheticInits.append((JCExpression) captured_local);
339 }
340 }
342 //then, determine the arguments to the indy call
343 List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev);
345 //build a sam instance using an indy call to the meta-factory
346 int refKind = referenceKind(sym);
348 //convert to an invokedynamic call
349 result = makeMetafactoryIndyCall(context, refKind, sym, indy_args);
350 }
352 private JCIdent makeThis(Type type, Symbol owner) {
353 VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC,
354 names._this,
355 type,
356 owner);
357 return make.Ident(_this);
358 }
360 /**
361 * Translate a method reference into an invokedynamic call to the
362 * meta-factory.
363 * @param tree
364 */
365 @Override
366 public void visitReference(JCMemberReference tree) {
367 ReferenceTranslationContext localContext = (ReferenceTranslationContext)context;
369 //first determine the method symbol to be used to generate the sam instance
370 //this is either the method reference symbol, or the bridged reference symbol
371 Symbol refSym = localContext.isSignaturePolymorphic()
372 ? localContext.sigPolySym
373 : tree.sym;
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 * Converts a method reference which cannot be used directly into a lambda
745 */
746 private class MemberReferenceToLambda {
748 private final JCMemberReference tree;
749 private final ReferenceTranslationContext localContext;
750 private final Symbol owner;
751 private final ListBuffer<JCExpression> args = new ListBuffer<>();
752 private final ListBuffer<JCVariableDecl> params = new ListBuffer<>();
754 private JCExpression receiverExpression = null;
756 MemberReferenceToLambda(JCMemberReference tree, ReferenceTranslationContext localContext, Symbol owner) {
757 this.tree = tree;
758 this.localContext = localContext;
759 this.owner = owner;
760 }
762 JCLambda lambda() {
763 int prevPos = make.pos;
764 try {
765 make.at(tree);
767 //body generation - this can be either a method call or a
768 //new instance creation expression, depending on the member reference kind
769 VarSymbol rcvr = addParametersReturnReceiver();
770 JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE)
771 ? expressionInvoke(rcvr)
772 : expressionNew();
774 JCLambda slam = make.Lambda(params.toList(), expr);
775 slam.targets = tree.targets;
776 slam.type = tree.type;
777 slam.pos = tree.pos;
778 return slam;
779 } finally {
780 make.at(prevPos);
781 }
782 }
784 /**
785 * Generate the parameter list for the converted member reference.
786 *
787 * @return The receiver variable symbol, if any
788 */
789 VarSymbol addParametersReturnReceiver() {
790 Type samDesc = localContext.bridgedRefSig();
791 List<Type> samPTypes = samDesc.getParameterTypes();
792 List<Type> descPTypes = tree.getDescriptorType(types).getParameterTypes();
794 // Determine the receiver, if any
795 VarSymbol rcvr;
796 switch (tree.kind) {
797 case BOUND:
798 // The receiver is explicit in the method reference
799 rcvr = addParameter("rec$", tree.getQualifierExpression().type, false);
800 receiverExpression = attr.makeNullCheck(tree.getQualifierExpression());
801 break;
802 case UNBOUND:
803 // The receiver is the first parameter, extract it and
804 // adjust the SAM and unerased type lists accordingly
805 rcvr = addParameter("rec$", samDesc.getParameterTypes().head, false);
806 samPTypes = samPTypes.tail;
807 descPTypes = descPTypes.tail;
808 break;
809 default:
810 rcvr = null;
811 break;
812 }
813 List<Type> implPTypes = tree.sym.type.getParameterTypes();
814 int implSize = implPTypes.size();
815 int samSize = samPTypes.size();
816 // Last parameter to copy from referenced method, exclude final var args
817 int last = localContext.needsVarArgsConversion() ? implSize - 1 : implSize;
819 // Failsafe -- assure match-up
820 boolean checkForIntersection = tree.varargsElement != null || implSize == descPTypes.size();
822 // Use parameter types of the implementation method unless the unerased
823 // SAM parameter type is an intersection type, in that case use the
824 // erased SAM parameter type so that the supertype relationship
825 // the implementation method parameters is not obscured.
826 // Note: in this loop, the lists implPTypes, samPTypes, and descPTypes
827 // are used as pointers to the current parameter type information
828 // and are thus not usable afterwards.
829 for (int i = 0; implPTypes.nonEmpty() && i < last; ++i) {
830 // By default use the implementation method parmeter type
831 Type parmType = implPTypes.head;
832 // If the unerased parameter type is a type variable whose
833 // bound is an intersection (eg. <T extends A & B>) then
834 // use the SAM parameter type
835 if (checkForIntersection && descPTypes.head.getKind() == TypeKind.TYPEVAR) {
836 TypeVar tv = (TypeVar) descPTypes.head;
837 if (tv.bound.getKind() == TypeKind.INTERSECTION) {
838 parmType = samPTypes.head;
839 }
840 }
841 addParameter("x$" + i, parmType, true);
843 // Advance to the next parameter
844 implPTypes = implPTypes.tail;
845 samPTypes = samPTypes.tail;
846 descPTypes = descPTypes.tail;
847 }
848 // Flatten out the var args
849 for (int i = last; i < samSize; ++i) {
850 addParameter("xva$" + i, tree.varargsElement, true);
851 }
853 return rcvr;
854 }
856 JCExpression getReceiverExpression() {
857 return receiverExpression;
858 }
860 private JCExpression makeReceiver(VarSymbol rcvr) {
861 if (rcvr == null) return null;
862 JCExpression rcvrExpr = make.Ident(rcvr);
863 Type rcvrType = tree.sym.enclClass().type;
864 if (rcvrType == syms.arrayClass.type) {
865 // Map the receiver type to the actually type, not just "array"
866 rcvrType = tree.getQualifierExpression().type;
867 }
868 if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) {
869 rcvrExpr = make.TypeCast(make.Type(rcvrType), rcvrExpr).setType(rcvrType);
870 }
871 return rcvrExpr;
872 }
874 /**
875 * determine the receiver of the method call - the receiver can
876 * be a type qualifier, the synthetic receiver parameter or 'super'.
877 */
878 private JCExpression expressionInvoke(VarSymbol rcvr) {
879 JCExpression qualifier =
880 tree.sym.isStatic() ?
881 make.Type(tree.sym.owner.type) :
882 (rcvr != null) ?
883 makeReceiver(rcvr) :
884 tree.getQualifierExpression();
886 //create the qualifier expression
887 JCFieldAccess select = make.Select(qualifier, tree.sym.name);
888 select.sym = tree.sym;
889 select.type = tree.sym.erasure(types);
891 //create the method call expression
892 JCExpression apply = make.Apply(List.<JCExpression>nil(), select,
893 convertArgs(tree.sym, args.toList(), tree.varargsElement)).
894 setType(tree.sym.erasure(types).getReturnType());
896 apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType());
897 setVarargsIfNeeded(apply, tree.varargsElement);
898 return apply;
899 }
901 /**
902 * Lambda body to use for a 'new'.
903 */
904 private JCExpression expressionNew() {
905 if (tree.kind == ReferenceKind.ARRAY_CTOR) {
906 //create the array creation expression
907 JCNewArray newArr = make.NewArray(
908 make.Type(types.elemtype(tree.getQualifierExpression().type)),
909 List.of(make.Ident(params.first())),
910 null);
911 newArr.type = tree.getQualifierExpression().type;
912 return newArr;
913 } else {
914 //create the instance creation expression
915 //note that method reference syntax does not allow an explicit
916 //enclosing class (so the enclosing class is null)
917 JCNewClass newClass = make.NewClass(null,
918 List.<JCExpression>nil(),
919 make.Type(tree.getQualifierExpression().type),
920 convertArgs(tree.sym, args.toList(), tree.varargsElement),
921 null);
922 newClass.constructor = tree.sym;
923 newClass.constructorType = tree.sym.erasure(types);
924 newClass.type = tree.getQualifierExpression().type;
925 setVarargsIfNeeded(newClass, tree.varargsElement);
926 return newClass;
927 }
928 }
930 private VarSymbol addParameter(String name, Type p, boolean genArg) {
931 VarSymbol vsym = new VarSymbol(PARAMETER | SYNTHETIC, names.fromString(name), p, owner);
932 vsym.pos = tree.pos;
933 params.append(make.VarDef(vsym, null));
934 if (genArg) {
935 args.append(make.Ident(vsym));
936 }
937 return vsym;
938 }
939 }
941 private MethodType typeToMethodType(Type mt) {
942 Type type = types.erasure(mt);
943 return new MethodType(type.getParameterTypes(),
944 type.getReturnType(),
945 type.getThrownTypes(),
946 syms.methodClass);
947 }
949 /**
950 * Generate an indy method call to the meta factory
951 */
952 private JCExpression makeMetafactoryIndyCall(TranslationContext<?> context,
953 int refKind, Symbol refSym, List<JCExpression> indy_args) {
954 JCFunctionalExpression tree = context.tree;
955 //determine the static bsm args
956 MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
957 List<Object> staticArgs = List.<Object>of(
958 typeToMethodType(samSym.type),
959 new Pool.MethodHandle(refKind, refSym, types),
960 typeToMethodType(tree.getDescriptorType(types)));
962 //computed indy arg types
963 ListBuffer<Type> indy_args_types = new ListBuffer<>();
964 for (JCExpression arg : indy_args) {
965 indy_args_types.append(arg.type);
966 }
968 //finally, compute the type of the indy call
969 MethodType indyType = new MethodType(indy_args_types.toList(),
970 tree.type,
971 List.<Type>nil(),
972 syms.methodClass);
974 Name metafactoryName = context.needsAltMetafactory() ?
975 names.altMetafactory : names.metafactory;
977 if (context.needsAltMetafactory()) {
978 ListBuffer<Object> markers = new ListBuffer<>();
979 for (Type t : tree.targets.tail) {
980 if (t.tsym != syms.serializableType.tsym) {
981 markers.append(t.tsym);
982 }
983 }
984 int flags = context.isSerializable() ? FLAG_SERIALIZABLE : 0;
985 boolean hasMarkers = markers.nonEmpty();
986 boolean hasBridges = context.bridges.nonEmpty();
987 if (hasMarkers) {
988 flags |= FLAG_MARKERS;
989 }
990 if (hasBridges) {
991 flags |= FLAG_BRIDGES;
992 }
993 staticArgs = staticArgs.append(flags);
994 if (hasMarkers) {
995 staticArgs = staticArgs.append(markers.length());
996 staticArgs = staticArgs.appendList(markers.toList());
997 }
998 if (hasBridges) {
999 staticArgs = staticArgs.append(context.bridges.length() - 1);
1000 for (Symbol s : context.bridges) {
1001 Type s_erasure = s.erasure(types);
1002 if (!types.isSameType(s_erasure, samSym.erasure(types))) {
1003 staticArgs = staticArgs.append(s.erasure(types));
1004 }
1005 }
1006 }
1007 if (context.isSerializable()) {
1008 int prevPos = make.pos;
1009 try {
1010 make.at(kInfo.clazz);
1011 addDeserializationCase(refKind, refSym, tree.type, samSym,
1012 tree, staticArgs, indyType);
1013 } finally {
1014 make.at(prevPos);
1015 }
1016 }
1017 }
1019 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args, samSym.name);
1020 }
1022 /**
1023 * Generate an indy method call with given name, type and static bootstrap
1024 * arguments types
1025 */
1026 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
1027 List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs,
1028 Name methName) {
1029 int prevPos = make.pos;
1030 try {
1031 make.at(pos);
1032 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
1033 syms.stringType,
1034 syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs));
1036 Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site,
1037 bsmName, bsm_staticArgs, List.<Type>nil());
1039 DynamicMethodSymbol dynSym =
1040 new DynamicMethodSymbol(methName,
1041 syms.noSymbol,
1042 bsm.isStatic() ?
1043 ClassFile.REF_invokeStatic :
1044 ClassFile.REF_invokeVirtual,
1045 (MethodSymbol)bsm,
1046 indyType,
1047 staticArgs.toArray());
1049 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName);
1050 qualifier.sym = dynSym;
1051 qualifier.type = indyType.getReturnType();
1053 JCMethodInvocation proxyCall = make.Apply(List.<JCExpression>nil(), qualifier, indyArgs);
1054 proxyCall.type = indyType.getReturnType();
1055 return proxyCall;
1056 } finally {
1057 make.at(prevPos);
1058 }
1059 }
1060 //where
1061 private List<Type> bsmStaticArgToTypes(List<Object> args) {
1062 ListBuffer<Type> argtypes = new ListBuffer<>();
1063 for (Object arg : args) {
1064 argtypes.append(bsmStaticArgToType(arg));
1065 }
1066 return argtypes.toList();
1067 }
1069 private Type bsmStaticArgToType(Object arg) {
1070 Assert.checkNonNull(arg);
1071 if (arg instanceof ClassSymbol) {
1072 return syms.classType;
1073 } else if (arg instanceof Integer) {
1074 return syms.intType;
1075 } else if (arg instanceof Long) {
1076 return syms.longType;
1077 } else if (arg instanceof Float) {
1078 return syms.floatType;
1079 } else if (arg instanceof Double) {
1080 return syms.doubleType;
1081 } else if (arg instanceof String) {
1082 return syms.stringType;
1083 } else if (arg instanceof Pool.MethodHandle) {
1084 return syms.methodHandleType;
1085 } else if (arg instanceof MethodType) {
1086 return syms.methodTypeType;
1087 } else {
1088 Assert.error("bad static arg " + arg.getClass());
1089 return null;
1090 }
1091 }
1093 /**
1094 * Get the opcode associated with this method reference
1095 */
1096 private int referenceKind(Symbol refSym) {
1097 if (refSym.isConstructor()) {
1098 return ClassFile.REF_newInvokeSpecial;
1099 } else {
1100 if (refSym.isStatic()) {
1101 return ClassFile.REF_invokeStatic;
1102 } else if ((refSym.flags() & PRIVATE) != 0) {
1103 return ClassFile.REF_invokeSpecial;
1104 } else if (refSym.enclClass().isInterface()) {
1105 return ClassFile.REF_invokeInterface;
1106 } else {
1107 return ClassFile.REF_invokeVirtual;
1108 }
1109 }
1110 }
1112 // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer">
1113 /**
1114 * This visitor collects information about translation of a lambda expression.
1115 * More specifically, it keeps track of the enclosing contexts and captured locals
1116 * accessed by the lambda being translated (as well as other useful info).
1117 * It also translates away problems for LambdaToMethod.
1118 */
1119 class LambdaAnalyzerPreprocessor extends TreeTranslator {
1121 /** the frame stack - used to reconstruct translation info about enclosing scopes */
1122 private List<Frame> frameStack;
1124 /**
1125 * keep the count of lambda expression (used to generate unambiguous
1126 * names)
1127 */
1128 private int lambdaCount = 0;
1130 /**
1131 * keep the count of lambda expression defined in given context (used to
1132 * generate unambiguous names for serializable lambdas)
1133 */
1134 private class SyntheticMethodNameCounter {
1135 private Map<String, Integer> map = new HashMap<>();
1136 int getIndex(StringBuilder buf) {
1137 String temp = buf.toString();
1138 Integer count = map.get(temp);
1139 if (count == null) {
1140 count = 0;
1141 }
1142 ++count;
1143 map.put(temp, count);
1144 return count;
1145 }
1146 }
1147 private SyntheticMethodNameCounter syntheticMethodNameCounts =
1148 new SyntheticMethodNameCounter();
1150 private Map<Symbol, JCClassDecl> localClassDefs;
1152 /**
1153 * maps for fake clinit symbols to be used as owners of lambda occurring in
1154 * a static var init context
1155 */
1156 private Map<ClassSymbol, Symbol> clinits =
1157 new HashMap<ClassSymbol, Symbol>();
1159 private JCClassDecl analyzeAndPreprocessClass(JCClassDecl tree) {
1160 frameStack = List.nil();
1161 localClassDefs = new HashMap<Symbol, JCClassDecl>();
1162 return translate(tree);
1163 }
1165 @Override
1166 public void visitBlock(JCBlock tree) {
1167 List<Frame> prevStack = frameStack;
1168 try {
1169 if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) {
1170 frameStack = frameStack.prepend(new Frame(tree));
1171 }
1172 super.visitBlock(tree);
1173 }
1174 finally {
1175 frameStack = prevStack;
1176 }
1177 }
1179 @Override
1180 public void visitClassDef(JCClassDecl tree) {
1181 List<Frame> prevStack = frameStack;
1182 SyntheticMethodNameCounter prevSyntheticMethodNameCounts =
1183 syntheticMethodNameCounts;
1184 Map<ClassSymbol, Symbol> prevClinits = clinits;
1185 DiagnosticSource prevSource = log.currentSource();
1186 try {
1187 log.useSource(tree.sym.sourcefile);
1188 syntheticMethodNameCounts = new SyntheticMethodNameCounter();
1189 prevClinits = new HashMap<ClassSymbol, Symbol>();
1190 if (tree.sym.owner.kind == MTH) {
1191 localClassDefs.put(tree.sym, tree);
1192 }
1193 if (directlyEnclosingLambda() != null) {
1194 tree.sym.owner = owner();
1195 if (tree.sym.hasOuterInstance()) {
1196 //if a class is defined within a lambda, the lambda must capture
1197 //its enclosing instance (if any)
1198 TranslationContext<?> localContext = context();
1199 while (localContext != null) {
1200 if (localContext.tree.getTag() == LAMBDA) {
1201 ((LambdaTranslationContext)localContext)
1202 .addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS);
1203 }
1204 localContext = localContext.prev;
1205 }
1206 }
1207 }
1208 frameStack = frameStack.prepend(new Frame(tree));
1209 super.visitClassDef(tree);
1210 }
1211 finally {
1212 log.useSource(prevSource.getFile());
1213 frameStack = prevStack;
1214 syntheticMethodNameCounts = prevSyntheticMethodNameCounts;
1215 clinits = prevClinits;
1216 }
1217 }
1219 @Override
1220 public void visitIdent(JCIdent tree) {
1221 if (context() != null && lambdaIdentSymbolFilter(tree.sym)) {
1222 if (tree.sym.kind == VAR &&
1223 tree.sym.owner.kind == MTH &&
1224 tree.type.constValue() == null) {
1225 TranslationContext<?> localContext = context();
1226 while (localContext != null) {
1227 if (localContext.tree.getTag() == LAMBDA) {
1228 JCTree block = capturedDecl(localContext.depth, tree.sym);
1229 if (block == null) break;
1230 ((LambdaTranslationContext)localContext)
1231 .addSymbol(tree.sym, CAPTURED_VAR);
1232 }
1233 localContext = localContext.prev;
1234 }
1235 } else if (tree.sym.owner.kind == TYP) {
1236 TranslationContext<?> localContext = context();
1237 while (localContext != null) {
1238 if (localContext.tree.hasTag(LAMBDA)) {
1239 JCTree block = capturedDecl(localContext.depth, tree.sym);
1240 if (block == null) break;
1241 switch (block.getTag()) {
1242 case CLASSDEF:
1243 JCClassDecl cdecl = (JCClassDecl)block;
1244 ((LambdaTranslationContext)localContext)
1245 .addSymbol(cdecl.sym, CAPTURED_THIS);
1246 break;
1247 default:
1248 Assert.error("bad block kind");
1249 }
1250 }
1251 localContext = localContext.prev;
1252 }
1253 }
1254 }
1255 super.visitIdent(tree);
1256 }
1258 @Override
1259 public void visitLambda(JCLambda tree) {
1260 analyzeLambda(tree, "lambda.stat");
1261 }
1263 private void analyzeLambda(JCLambda tree, JCExpression methodReferenceReceiver) {
1264 // Translation of the receiver expression must occur first
1265 JCExpression rcvr = translate(methodReferenceReceiver);
1266 LambdaTranslationContext context = analyzeLambda(tree, "mref.stat.1");
1267 if (rcvr != null) {
1268 context.methodReferenceReceiver = rcvr;
1269 }
1270 }
1272 private LambdaTranslationContext analyzeLambda(JCLambda tree, String statKey) {
1273 List<Frame> prevStack = frameStack;
1274 try {
1275 LambdaTranslationContext context = new LambdaTranslationContext(tree);
1276 if (dumpLambdaToMethodStats) {
1277 log.note(tree, statKey, context.needsAltMetafactory(), context.translatedSym);
1278 }
1279 frameStack = frameStack.prepend(new Frame(tree));
1280 for (JCVariableDecl param : tree.params) {
1281 context.addSymbol(param.sym, PARAM);
1282 frameStack.head.addLocal(param.sym);
1283 }
1284 contextMap.put(tree, context);
1285 super.visitLambda(tree);
1286 context.complete();
1287 return context;
1288 }
1289 finally {
1290 frameStack = prevStack;
1291 }
1292 }
1294 @Override
1295 public void visitMethodDef(JCMethodDecl tree) {
1296 List<Frame> prevStack = frameStack;
1297 try {
1298 frameStack = frameStack.prepend(new Frame(tree));
1299 super.visitMethodDef(tree);
1300 }
1301 finally {
1302 frameStack = prevStack;
1303 }
1304 }
1306 @Override
1307 public void visitNewClass(JCNewClass tree) {
1308 TypeSymbol def = tree.type.tsym;
1309 boolean inReferencedClass = currentlyInClass(def);
1310 boolean isLocal = def.isLocal();
1311 if ((inReferencedClass && isLocal || lambdaNewClassFilter(context(), tree))) {
1312 TranslationContext<?> localContext = context();
1313 while (localContext != null) {
1314 if (localContext.tree.getTag() == LAMBDA) {
1315 ((LambdaTranslationContext)localContext)
1316 .addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS);
1317 }
1318 localContext = localContext.prev;
1319 }
1320 }
1321 if (context() != null && !inReferencedClass && isLocal) {
1322 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context();
1323 captureLocalClassDefs(def, lambdaContext);
1324 }
1325 super.visitNewClass(tree);
1326 }
1327 //where
1328 void captureLocalClassDefs(Symbol csym, final LambdaTranslationContext lambdaContext) {
1329 JCClassDecl localCDef = localClassDefs.get(csym);
1330 if (localCDef != null && lambdaContext.freeVarProcessedLocalClasses.add(csym)) {
1331 BasicFreeVarCollector fvc = lower.new BasicFreeVarCollector() {
1332 @Override
1333 void addFreeVars(ClassSymbol c) {
1334 captureLocalClassDefs(c, lambdaContext);
1335 }
1336 @Override
1337 void visitSymbol(Symbol sym) {
1338 if (sym.kind == VAR &&
1339 sym.owner.kind == MTH &&
1340 ((VarSymbol)sym).getConstValue() == null) {
1341 TranslationContext<?> localContext = context();
1342 while (localContext != null) {
1343 if (localContext.tree.getTag() == LAMBDA) {
1344 JCTree block = capturedDecl(localContext.depth, sym);
1345 if (block == null) break;
1346 ((LambdaTranslationContext)localContext).addSymbol(sym, CAPTURED_VAR);
1347 }
1348 localContext = localContext.prev;
1349 }
1350 }
1351 }
1352 };
1353 fvc.scan(localCDef);
1354 }
1355 }
1356 //where
1357 boolean currentlyInClass(Symbol csym) {
1358 for (Frame frame : frameStack) {
1359 if (frame.tree.hasTag(JCTree.Tag.CLASSDEF)) {
1360 JCClassDecl cdef = (JCClassDecl) frame.tree;
1361 if (cdef.sym == csym) {
1362 return true;
1363 }
1364 }
1365 }
1366 return false;
1367 }
1369 /**
1370 * Method references to local class constructors, may, if the local
1371 * class references local variables, have implicit constructor
1372 * parameters added in Lower; As a result, the invokedynamic bootstrap
1373 * information added in the LambdaToMethod pass will have the wrong
1374 * signature. Hooks between Lower and LambdaToMethod have been added to
1375 * handle normal "new" in this case. This visitor converts potentially
1376 * affected method references into a lambda containing a normal
1377 * expression.
1378 *
1379 * @param tree
1380 */
1381 @Override
1382 public void visitReference(JCMemberReference tree) {
1383 ReferenceTranslationContext rcontext = new ReferenceTranslationContext(tree);
1384 contextMap.put(tree, rcontext);
1385 if (rcontext.needsConversionToLambda()) {
1386 // Convert to a lambda, and process as such
1387 MemberReferenceToLambda conv = new MemberReferenceToLambda(tree, rcontext, owner());
1388 analyzeLambda(conv.lambda(), conv.getReceiverExpression());
1389 } else {
1390 super.visitReference(tree);
1391 if (dumpLambdaToMethodStats) {
1392 log.note(tree, "mref.stat", rcontext.needsAltMetafactory(), null);
1393 }
1394 }
1395 }
1397 @Override
1398 public void visitSelect(JCFieldAccess tree) {
1399 if (context() != null && tree.sym.kind == VAR &&
1400 (tree.sym.name == names._this ||
1401 tree.sym.name == names._super)) {
1402 // A select of this or super means, if we are in a lambda,
1403 // we much have an instance context
1404 TranslationContext<?> localContext = context();
1405 while (localContext != null) {
1406 if (localContext.tree.hasTag(LAMBDA)) {
1407 JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym);
1408 if (clazz == null) break;
1409 ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS);
1410 }
1411 localContext = localContext.prev;
1412 }
1413 }
1414 super.visitSelect(tree);
1415 }
1417 @Override
1418 public void visitVarDef(JCVariableDecl tree) {
1419 TranslationContext<?> context = context();
1420 LambdaTranslationContext ltc = (context != null && context instanceof LambdaTranslationContext)?
1421 (LambdaTranslationContext)context :
1422 null;
1423 if (ltc != null) {
1424 if (frameStack.head.tree.hasTag(LAMBDA)) {
1425 ltc.addSymbol(tree.sym, LOCAL_VAR);
1426 }
1427 // Check for type variables (including as type arguments).
1428 // If they occur within class nested in a lambda, mark for erasure
1429 Type type = tree.sym.asType();
1430 if (inClassWithinLambda() && !types.isSameType(types.erasure(type), type)) {
1431 ltc.addSymbol(tree.sym, TYPE_VAR);
1432 }
1433 }
1435 List<Frame> prevStack = frameStack;
1436 try {
1437 if (tree.sym.owner.kind == MTH) {
1438 frameStack.head.addLocal(tree.sym);
1439 }
1440 frameStack = frameStack.prepend(new Frame(tree));
1441 super.visitVarDef(tree);
1442 }
1443 finally {
1444 frameStack = prevStack;
1445 }
1446 }
1448 /**
1449 * Return a valid owner given the current declaration stack
1450 * (required to skip synthetic lambda symbols)
1451 */
1452 private Symbol owner() {
1453 return owner(false);
1454 }
1456 @SuppressWarnings("fallthrough")
1457 private Symbol owner(boolean skipLambda) {
1458 List<Frame> frameStack2 = frameStack;
1459 while (frameStack2.nonEmpty()) {
1460 switch (frameStack2.head.tree.getTag()) {
1461 case VARDEF:
1462 if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) {
1463 frameStack2 = frameStack2.tail;
1464 break;
1465 }
1466 JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree;
1467 return initSym(cdecl.sym,
1468 ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC);
1469 case BLOCK:
1470 JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree;
1471 return initSym(cdecl2.sym,
1472 ((JCBlock)frameStack2.head.tree).flags & STATIC);
1473 case CLASSDEF:
1474 return ((JCClassDecl)frameStack2.head.tree).sym;
1475 case METHODDEF:
1476 return ((JCMethodDecl)frameStack2.head.tree).sym;
1477 case LAMBDA:
1478 if (!skipLambda)
1479 return ((LambdaTranslationContext)contextMap
1480 .get(frameStack2.head.tree)).translatedSym;
1481 default:
1482 frameStack2 = frameStack2.tail;
1483 }
1484 }
1485 Assert.error();
1486 return null;
1487 }
1489 private Symbol initSym(ClassSymbol csym, long flags) {
1490 boolean isStatic = (flags & STATIC) != 0;
1491 if (isStatic) {
1492 /* static clinits are generated in Gen, so we need to use a fake
1493 * one. Attr creates a fake clinit method while attributing
1494 * lambda expressions used as initializers of static fields, so
1495 * let's use that one.
1496 */
1497 MethodSymbol clinit = attr.removeClinit(csym);
1498 if (clinit != null) {
1499 clinits.put(csym, clinit);
1500 return clinit;
1501 }
1503 /* if no clinit is found at Attr, then let's try at clinits.
1504 */
1505 clinit = (MethodSymbol)clinits.get(csym);
1506 if (clinit == null) {
1507 /* no luck, let's create a new one
1508 */
1509 clinit = makePrivateSyntheticMethod(STATIC,
1510 names.clinit,
1511 new MethodType(List.<Type>nil(), syms.voidType,
1512 List.<Type>nil(), syms.methodClass),
1513 csym);
1514 clinits.put(csym, clinit);
1515 }
1516 return clinit;
1517 } else {
1518 //get the first constructor and treat it as the instance init sym
1519 for (Symbol s : csym.members_field.getElementsByName(names.init)) {
1520 return s;
1521 }
1522 }
1523 Assert.error("init not found");
1524 return null;
1525 }
1527 private JCTree directlyEnclosingLambda() {
1528 if (frameStack.isEmpty()) {
1529 return null;
1530 }
1531 List<Frame> frameStack2 = frameStack;
1532 while (frameStack2.nonEmpty()) {
1533 switch (frameStack2.head.tree.getTag()) {
1534 case CLASSDEF:
1535 case METHODDEF:
1536 return null;
1537 case LAMBDA:
1538 return frameStack2.head.tree;
1539 default:
1540 frameStack2 = frameStack2.tail;
1541 }
1542 }
1543 Assert.error();
1544 return null;
1545 }
1547 private boolean inClassWithinLambda() {
1548 if (frameStack.isEmpty()) {
1549 return false;
1550 }
1551 List<Frame> frameStack2 = frameStack;
1552 boolean classFound = false;
1553 while (frameStack2.nonEmpty()) {
1554 switch (frameStack2.head.tree.getTag()) {
1555 case LAMBDA:
1556 return classFound;
1557 case CLASSDEF:
1558 classFound = true;
1559 frameStack2 = frameStack2.tail;
1560 break;
1561 default:
1562 frameStack2 = frameStack2.tail;
1563 }
1564 }
1565 // No lambda
1566 return false;
1567 }
1569 /**
1570 * Return the declaration corresponding to a symbol in the enclosing
1571 * scope; the depth parameter is used to filter out symbols defined
1572 * in nested scopes (which do not need to undergo capture).
1573 */
1574 private JCTree capturedDecl(int depth, Symbol sym) {
1575 int currentDepth = frameStack.size() - 1;
1576 for (Frame block : frameStack) {
1577 switch (block.tree.getTag()) {
1578 case CLASSDEF:
1579 ClassSymbol clazz = ((JCClassDecl)block.tree).sym;
1580 if (sym.isMemberOf(clazz, types)) {
1581 return currentDepth > depth ? null : block.tree;
1582 }
1583 break;
1584 case VARDEF:
1585 if (((JCVariableDecl)block.tree).sym == sym &&
1586 sym.owner.kind == MTH) { //only locals are captured
1587 return currentDepth > depth ? null : block.tree;
1588 }
1589 break;
1590 case BLOCK:
1591 case METHODDEF:
1592 case LAMBDA:
1593 if (block.locals != null && block.locals.contains(sym)) {
1594 return currentDepth > depth ? null : block.tree;
1595 }
1596 break;
1597 default:
1598 Assert.error("bad decl kind " + block.tree.getTag());
1599 }
1600 currentDepth--;
1601 }
1602 return null;
1603 }
1605 private TranslationContext<?> context() {
1606 for (Frame frame : frameStack) {
1607 TranslationContext<?> context = contextMap.get(frame.tree);
1608 if (context != null) {
1609 return context;
1610 }
1611 }
1612 return null;
1613 }
1615 /**
1616 * This is used to filter out those identifiers that needs to be adjusted
1617 * when translating away lambda expressions
1618 */
1619 private boolean lambdaIdentSymbolFilter(Symbol sym) {
1620 return (sym.kind == VAR || sym.kind == MTH)
1621 && !sym.isStatic()
1622 && sym.name != names.init;
1623 }
1625 /**
1626 * This is used to filter out those new class expressions that need to
1627 * be qualified with an enclosing tree
1628 */
1629 private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) {
1630 if (context != null
1631 && tree.encl == null
1632 && tree.def == null
1633 && !tree.type.getEnclosingType().hasTag(NONE)) {
1634 Type encl = tree.type.getEnclosingType();
1635 Type current = context.owner.enclClass().type;
1636 while (!current.hasTag(NONE)) {
1637 if (current.tsym.isSubClass(encl.tsym, types)) {
1638 return true;
1639 }
1640 current = current.getEnclosingType();
1641 }
1642 return false;
1643 } else {
1644 return false;
1645 }
1646 }
1648 private class Frame {
1649 final JCTree tree;
1650 List<Symbol> locals;
1652 public Frame(JCTree tree) {
1653 this.tree = tree;
1654 }
1656 void addLocal(Symbol sym) {
1657 if (locals == null) {
1658 locals = List.nil();
1659 }
1660 locals = locals.prepend(sym);
1661 }
1662 }
1664 /**
1665 * This class is used to store important information regarding translation of
1666 * lambda expression/method references (see subclasses).
1667 */
1668 private abstract class TranslationContext<T extends JCFunctionalExpression> {
1670 /** the underlying (untranslated) tree */
1671 final T tree;
1673 /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */
1674 final Symbol owner;
1676 /** the depth of this lambda expression in the frame stack */
1677 final int depth;
1679 /** the enclosing translation context (set for nested lambdas/mref) */
1680 final TranslationContext<?> prev;
1682 /** list of methods to be bridged by the meta-factory */
1683 final List<Symbol> bridges;
1685 TranslationContext(T tree) {
1686 this.tree = tree;
1687 this.owner = owner();
1688 this.depth = frameStack.size() - 1;
1689 this.prev = context();
1690 ClassSymbol csym =
1691 types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.targets, ABSTRACT | INTERFACE);
1692 this.bridges = types.functionalInterfaceBridges(csym);
1693 }
1695 /** does this functional expression need to be created using alternate metafactory? */
1696 boolean needsAltMetafactory() {
1697 return tree.targets.length() > 1 ||
1698 isSerializable() ||
1699 bridges.length() > 1;
1700 }
1702 /** does this functional expression require serialization support? */
1703 boolean isSerializable() {
1704 if (forceSerializable) {
1705 return true;
1706 }
1707 for (Type target : tree.targets) {
1708 if (types.asSuper(target, syms.serializableType.tsym) != null) {
1709 return true;
1710 }
1711 }
1712 return false;
1713 }
1715 /**
1716 * @return Name of the enclosing method to be folded into synthetic
1717 * method name
1718 */
1719 String enclosingMethodName() {
1720 return syntheticMethodNameComponent(owner.name);
1721 }
1723 /**
1724 * @return Method name in a form that can be folded into a
1725 * component of a synthetic method name
1726 */
1727 String syntheticMethodNameComponent(Name name) {
1728 if (name == null) {
1729 return "null";
1730 }
1731 String methodName = name.toString();
1732 if (methodName.equals("<clinit>")) {
1733 methodName = "static";
1734 } else if (methodName.equals("<init>")) {
1735 methodName = "new";
1736 }
1737 return methodName;
1738 }
1739 }
1741 /**
1742 * This class retains all the useful information about a lambda expression;
1743 * the contents of this class are filled by the LambdaAnalyzer visitor,
1744 * and the used by the main translation routines in order to adjust references
1745 * to captured locals/members, etc.
1746 */
1747 private class LambdaTranslationContext extends TranslationContext<JCLambda> {
1749 /** variable in the enclosing context to which this lambda is assigned */
1750 final Symbol self;
1752 /** variable in the enclosing context to which this lambda is assigned */
1753 final Symbol assignedTo;
1755 Map<LambdaSymbolKind, Map<Symbol, Symbol>> translatedSymbols;
1757 /** the synthetic symbol for the method hoisting the translated lambda */
1758 Symbol translatedSym;
1760 List<JCVariableDecl> syntheticParams;
1762 /**
1763 * to prevent recursion, track local classes processed
1764 */
1765 final Set<Symbol> freeVarProcessedLocalClasses;
1767 /**
1768 * For method references converted to lambdas. The method
1769 * reference receiver expression. Must be treated like a captured
1770 * variable.
1771 */
1772 JCExpression methodReferenceReceiver;
1774 LambdaTranslationContext(JCLambda tree) {
1775 super(tree);
1776 Frame frame = frameStack.head;
1777 switch (frame.tree.getTag()) {
1778 case VARDEF:
1779 assignedTo = self = ((JCVariableDecl) frame.tree).sym;
1780 break;
1781 case ASSIGN:
1782 self = null;
1783 assignedTo = TreeInfo.symbol(((JCAssign) frame.tree).getVariable());
1784 break;
1785 default:
1786 assignedTo = self = null;
1787 break;
1788 }
1790 // This symbol will be filled-in in complete
1791 this.translatedSym = makePrivateSyntheticMethod(0, null, null, owner.enclClass());
1793 translatedSymbols = new EnumMap<>(LambdaSymbolKind.class);
1795 translatedSymbols.put(PARAM, new LinkedHashMap<Symbol, Symbol>());
1796 translatedSymbols.put(LOCAL_VAR, new LinkedHashMap<Symbol, Symbol>());
1797 translatedSymbols.put(CAPTURED_VAR, new LinkedHashMap<Symbol, Symbol>());
1798 translatedSymbols.put(CAPTURED_THIS, new LinkedHashMap<Symbol, Symbol>());
1799 translatedSymbols.put(TYPE_VAR, new LinkedHashMap<Symbol, Symbol>());
1801 freeVarProcessedLocalClasses = new HashSet<>();
1802 }
1804 /**
1805 * For a serializable lambda, generate a disambiguating string
1806 * which maximizes stability across deserialization.
1807 *
1808 * @return String to differentiate synthetic lambda method names
1809 */
1810 private String serializedLambdaDisambiguation() {
1811 StringBuilder buf = new StringBuilder();
1812 // Append the enclosing method signature to differentiate
1813 // overloaded enclosing methods. For lambdas enclosed in
1814 // lambdas, the generated lambda method will not have type yet,
1815 // but the enclosing method's name will have been generated
1816 // with this same method, so it will be unique and never be
1817 // overloaded.
1818 Assert.check(
1819 owner.type != null ||
1820 directlyEnclosingLambda() != null);
1821 if (owner.type != null) {
1822 buf.append(typeSig(owner.type));
1823 buf.append(":");
1824 }
1826 // Add target type info
1827 buf.append(types.findDescriptorSymbol(tree.type.tsym).owner.flatName());
1828 buf.append(" ");
1830 // Add variable assigned to
1831 if (assignedTo != null) {
1832 buf.append(assignedTo.flatName());
1833 buf.append("=");
1834 }
1835 //add captured locals info: type, name, order
1836 for (Symbol fv : getSymbolMap(CAPTURED_VAR).keySet()) {
1837 if (fv != self) {
1838 buf.append(typeSig(fv.type));
1839 buf.append(" ");
1840 buf.append(fv.flatName());
1841 buf.append(",");
1842 }
1843 }
1845 return buf.toString();
1846 }
1848 /**
1849 * For a non-serializable lambda, generate a simple method.
1850 *
1851 * @return Name to use for the synthetic lambda method name
1852 */
1853 private Name lambdaName() {
1854 return names.lambda.append(names.fromString(enclosingMethodName() + "$" + lambdaCount++));
1855 }
1857 /**
1858 * For a serializable lambda, generate a method name which maximizes
1859 * name stability across deserialization.
1860 *
1861 * @return Name to use for the synthetic lambda method name
1862 */
1863 private Name serializedLambdaName() {
1864 StringBuilder buf = new StringBuilder();
1865 buf.append(names.lambda);
1866 // Append the name of the method enclosing the lambda.
1867 buf.append(enclosingMethodName());
1868 buf.append('$');
1869 // Append a hash of the disambiguating string : enclosing method
1870 // signature, etc.
1871 String disam = serializedLambdaDisambiguation();
1872 buf.append(Integer.toHexString(disam.hashCode()));
1873 buf.append('$');
1874 // The above appended name components may not be unique, append
1875 // a count based on the above name components.
1876 buf.append(syntheticMethodNameCounts.getIndex(buf));
1877 String result = buf.toString();
1878 //System.err.printf("serializedLambdaName: %s -- %s\n", result, disam);
1879 return names.fromString(result);
1880 }
1882 /**
1883 * Translate a symbol of a given kind into something suitable for the
1884 * synthetic lambda body
1885 */
1886 Symbol translate(Name name, final Symbol sym, LambdaSymbolKind skind) {
1887 Symbol ret;
1888 switch (skind) {
1889 case CAPTURED_THIS:
1890 ret = sym; // self represented
1891 break;
1892 case TYPE_VAR:
1893 // Just erase the type var
1894 ret = new VarSymbol(sym.flags(), name,
1895 types.erasure(sym.type), sym.owner);
1897 /* this information should also be kept for LVT generation at Gen
1898 * a Symbol with pos < startPos won't be tracked.
1899 */
1900 ((VarSymbol)ret).pos = ((VarSymbol)sym).pos;
1901 break;
1902 case CAPTURED_VAR:
1903 ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym) {
1904 @Override
1905 public Symbol baseSymbol() {
1906 //keep mapping with original captured symbol
1907 return sym;
1908 }
1909 };
1910 break;
1911 case LOCAL_VAR:
1912 ret = new VarSymbol(sym.flags() & FINAL, name, sym.type, translatedSym);
1913 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
1914 break;
1915 case PARAM:
1916 ret = new VarSymbol((sym.flags() & FINAL) | PARAMETER, name, types.erasure(sym.type), translatedSym);
1917 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
1918 break;
1919 default:
1920 ret = makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
1921 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
1922 }
1923 if (ret != sym) {
1924 ret.setDeclarationAttributes(sym.getRawAttributes());
1925 ret.setTypeAttributes(sym.getRawTypeAttributes());
1926 }
1927 return ret;
1928 }
1930 void addSymbol(Symbol sym, LambdaSymbolKind skind) {
1931 Map<Symbol, Symbol> transMap = getSymbolMap(skind);
1932 Name preferredName;
1933 switch (skind) {
1934 case CAPTURED_THIS:
1935 preferredName = names.fromString("encl$" + transMap.size());
1936 break;
1937 case CAPTURED_VAR:
1938 preferredName = names.fromString("cap$" + transMap.size());
1939 break;
1940 case LOCAL_VAR:
1941 preferredName = sym.name;
1942 break;
1943 case PARAM:
1944 preferredName = sym.name;
1945 break;
1946 case TYPE_VAR:
1947 preferredName = sym.name;
1948 break;
1949 default: throw new AssertionError();
1950 }
1951 if (!transMap.containsKey(sym)) {
1952 transMap.put(sym, translate(preferredName, sym, skind));
1953 }
1954 }
1956 Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind skind) {
1957 Map<Symbol, Symbol> m = translatedSymbols.get(skind);
1958 Assert.checkNonNull(m);
1959 return m;
1960 }
1962 JCTree translate(JCIdent lambdaIdent) {
1963 for (Map<Symbol, Symbol> m : translatedSymbols.values()) {
1964 if (m.containsKey(lambdaIdent.sym)) {
1965 Symbol tSym = m.get(lambdaIdent.sym);
1966 JCTree t = make.Ident(tSym).setType(lambdaIdent.type);
1967 tSym.setTypeAttributes(lambdaIdent.sym.getRawTypeAttributes());
1968 return t;
1969 }
1970 }
1971 return null;
1972 }
1974 /**
1975 * The translatedSym is not complete/accurate until the analysis is
1976 * finished. Once the analysis is finished, the translatedSym is
1977 * "completed" -- updated with type information, access modifiers,
1978 * and full parameter list.
1979 */
1980 void complete() {
1981 if (syntheticParams != null) {
1982 return;
1983 }
1984 boolean inInterface = translatedSym.owner.isInterface();
1985 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty();
1987 // If instance access isn't needed, make it static.
1988 // Interface instance methods must be default methods.
1989 // Lambda methods are private synthetic.
1990 // Inherit ACC_STRICT from the enclosing method, or, for clinit,
1991 // from the class.
1992 translatedSym.flags_field = SYNTHETIC | LAMBDA_METHOD |
1993 owner.flags_field & STRICTFP |
1994 owner.owner.flags_field & STRICTFP |
1995 PRIVATE |
1996 (thisReferenced? (inInterface? DEFAULT : 0) : STATIC);
1998 //compute synthetic params
1999 ListBuffer<JCVariableDecl> params = new ListBuffer<>();
2001 // The signature of the method is augmented with the following
2002 // synthetic parameters:
2003 //
2004 // 1) reference to enclosing contexts captured by the lambda expression
2005 // 2) enclosing locals captured by the lambda expression
2006 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR).values()) {
2007 params.append(make.VarDef((VarSymbol) thisSym, null));
2008 }
2009 if (methodReferenceReceiver != null) {
2010 params.append(make.VarDef(
2011 make.Modifiers(PARAMETER|FINAL),
2012 names.fromString("$rcvr$"),
2013 make.Type(methodReferenceReceiver.type),
2014 null));
2015 }
2016 for (Symbol thisSym : getSymbolMap(PARAM).values()) {
2017 params.append(make.VarDef((VarSymbol) thisSym, null));
2018 }
2019 syntheticParams = params.toList();
2021 // Compute and set the lambda name
2022 translatedSym.name = isSerializable()
2023 ? serializedLambdaName()
2024 : lambdaName();
2026 //prepend synthetic args to translated lambda method signature
2027 translatedSym.type = types.createMethodTypeWithParameters(
2028 generatedLambdaSig(),
2029 TreeInfo.types(syntheticParams));
2030 }
2032 Type generatedLambdaSig() {
2033 return types.erasure(tree.getDescriptorType(types));
2034 }
2035 }
2037 /**
2038 * This class retains all the useful information about a method reference;
2039 * the contents of this class are filled by the LambdaAnalyzer visitor,
2040 * and the used by the main translation routines in order to adjust method
2041 * references (i.e. in case a bridge is needed)
2042 */
2043 private final class ReferenceTranslationContext extends TranslationContext<JCMemberReference> {
2045 final boolean isSuper;
2046 final Symbol sigPolySym;
2048 ReferenceTranslationContext(JCMemberReference tree) {
2049 super(tree);
2050 this.isSuper = tree.hasKind(ReferenceKind.SUPER);
2051 this.sigPolySym = isSignaturePolymorphic()
2052 ? makePrivateSyntheticMethod(tree.sym.flags(),
2053 tree.sym.name,
2054 bridgedRefSig(),
2055 tree.sym.enclClass())
2056 : null;
2057 }
2059 /**
2060 * Get the opcode associated with this method reference
2061 */
2062 int referenceKind() {
2063 return LambdaToMethod.this.referenceKind(tree.sym);
2064 }
2066 boolean needsVarArgsConversion() {
2067 return tree.varargsElement != null;
2068 }
2070 /**
2071 * @return Is this an array operation like clone()
2072 */
2073 boolean isArrayOp() {
2074 return tree.sym.owner == syms.arrayClass;
2075 }
2077 boolean receiverAccessible() {
2078 //hack needed to workaround 292 bug (7087658)
2079 //when 292 issue is fixed we should remove this and change the backend
2080 //code to always generate a method handle to an accessible method
2081 return tree.ownerAccessible;
2082 }
2084 /**
2085 * The VM does not support access across nested classes (8010319).
2086 * Were that ever to change, this should be removed.
2087 */
2088 boolean isPrivateInOtherClass() {
2089 return (tree.sym.flags() & PRIVATE) != 0 &&
2090 !types.isSameType(
2091 types.erasure(tree.sym.enclClass().asType()),
2092 types.erasure(owner.enclClass().asType()));
2093 }
2095 /**
2096 * Signature polymorphic methods need special handling.
2097 * e.g. MethodHandle.invoke() MethodHandle.invokeExact()
2098 */
2099 final boolean isSignaturePolymorphic() {
2100 return tree.sym.kind == MTH &&
2101 types.isSignaturePolymorphic((MethodSymbol)tree.sym);
2102 }
2104 /**
2105 * Erasure destroys the implementation parameter subtype
2106 * relationship for intersection types
2107 */
2108 boolean interfaceParameterIsIntersectionType() {
2109 List<Type> tl = tree.getDescriptorType(types).getParameterTypes();
2110 if (tree.kind == ReferenceKind.UNBOUND) {
2111 tl = tl.tail;
2112 }
2113 for (; tl.nonEmpty(); tl = tl.tail) {
2114 Type pt = tl.head;
2115 if (pt.getKind() == TypeKind.TYPEVAR) {
2116 TypeVar tv = (TypeVar) pt;
2117 if (tv.bound.getKind() == TypeKind.INTERSECTION) {
2118 return true;
2119 }
2120 }
2121 }
2122 return false;
2123 }
2125 /**
2126 * Does this reference need to be converted to a lambda
2127 * (i.e. var args need to be expanded or "super" is used)
2128 */
2129 final boolean needsConversionToLambda() {
2130 return interfaceParameterIsIntersectionType() ||
2131 isSuper ||
2132 needsVarArgsConversion() ||
2133 isArrayOp() ||
2134 isPrivateInOtherClass() ||
2135 !receiverAccessible() ||
2136 (tree.getMode() == ReferenceMode.NEW &&
2137 tree.kind != ReferenceKind.ARRAY_CTOR &&
2138 (tree.sym.owner.isLocal() || tree.sym.owner.isInner()));
2139 }
2141 Type generatedRefSig() {
2142 return types.erasure(tree.sym.type);
2143 }
2145 Type bridgedRefSig() {
2146 return types.erasure(types.findDescriptorSymbol(tree.targets.head.tsym).type);
2147 }
2148 }
2149 }
2150 // </editor-fold>
2152 /*
2153 * These keys provide mappings for various translated lambda symbols
2154 * and the prevailing order must be maintained.
2155 */
2156 enum LambdaSymbolKind {
2157 PARAM, // original to translated lambda parameters
2158 LOCAL_VAR, // original to translated lambda locals
2159 CAPTURED_VAR, // variables in enclosing scope to translated synthetic parameters
2160 CAPTURED_THIS, // class symbols to translated synthetic parameters (for captured member access)
2161 TYPE_VAR; // original to translated lambda type variables
2162 }
2164 /**
2165 * ****************************************************************
2166 * Signature Generation
2167 * ****************************************************************
2168 */
2170 private String typeSig(Type type) {
2171 L2MSignatureGenerator sg = new L2MSignatureGenerator();
2172 sg.assembleSig(type);
2173 return sg.toString();
2174 }
2176 private String classSig(Type type) {
2177 L2MSignatureGenerator sg = new L2MSignatureGenerator();
2178 sg.assembleClassSig(type);
2179 return sg.toString();
2180 }
2182 /**
2183 * Signature Generation
2184 */
2185 private class L2MSignatureGenerator extends Types.SignatureGenerator {
2187 /**
2188 * An output buffer for type signatures.
2189 */
2190 StringBuilder sb = new StringBuilder();
2192 L2MSignatureGenerator() {
2193 super(types);
2194 }
2196 @Override
2197 protected void append(char ch) {
2198 sb.append(ch);
2199 }
2201 @Override
2202 protected void append(byte[] ba) {
2203 sb.append(new String(ba));
2204 }
2206 @Override
2207 protected void append(Name name) {
2208 sb.append(name.toString());
2209 }
2211 @Override
2212 public String toString() {
2213 return sb.toString();
2214 }
2215 }
2216 }