1 /*
2 * Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.tools.javac.comp;
27
28 import com.sun.tools.javac.code.Symbol.MethodHandleSymbol;
29 import com.sun.tools.javac.code.Types.SignatureGenerator.InvalidSignatureException;
30 import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant;
31 import com.sun.tools.javac.resources.CompilerProperties.Errors;
32 import com.sun.tools.javac.resources.CompilerProperties.Fragments;
33 import com.sun.tools.javac.tree.*;
34 import com.sun.tools.javac.tree.JCTree.*;
35 import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind;
36 import com.sun.tools.javac.tree.TreeMaker;
37 import com.sun.tools.javac.tree.TreeTranslator;
38 import com.sun.tools.javac.code.Attribute;
39 import com.sun.tools.javac.code.Symbol;
40 import com.sun.tools.javac.code.Symbol.ClassSymbol;
41 import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol;
42 import com.sun.tools.javac.code.Symbol.MethodSymbol;
43 import com.sun.tools.javac.code.Symbol.TypeSymbol;
44 import com.sun.tools.javac.code.Symbol.VarSymbol;
45 import com.sun.tools.javac.code.Symtab;
46 import com.sun.tools.javac.code.Type;
47 import com.sun.tools.javac.code.Type.MethodType;
48 import com.sun.tools.javac.code.Type.TypeVar;
49 import com.sun.tools.javac.code.Types;
50 import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzerPreprocessor.*;
51 import com.sun.tools.javac.comp.Lower.BasicFreeVarCollector;
52 import com.sun.tools.javac.resources.CompilerProperties.Notes;
53 import com.sun.tools.javac.jvm.*;
54 import com.sun.tools.javac.util.*;
55 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
56 import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
57
58 import java.util.EnumMap;
59 import java.util.HashMap;
60 import java.util.HashSet;
61 import java.util.LinkedHashMap;
62 import java.util.Map;
63 import java.util.Optional;
64 import java.util.Set;
65 import java.util.function.Consumer;
66 import java.util.function.Supplier;
67
68 import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*;
69 import static com.sun.tools.javac.code.Flags.*;
70 import static com.sun.tools.javac.code.Kinds.Kind.*;
71 import static com.sun.tools.javac.code.TypeTag.*;
72 import static com.sun.tools.javac.tree.JCTree.Tag.*;
73
74 import javax.lang.model.element.ElementKind;
75 import javax.lang.model.type.TypeKind;
76
77 import com.sun.tools.javac.main.Option;
78
79 /**
80 * This pass desugars lambda expressions into static methods
81 *
82 * <p><b>This is NOT part of any supported API.
83 * If you write code that depends on this, you do so at your own risk.
84 * This code and its internal interfaces are subject to change or
85 * deletion without notice.</b>
86 */
87 public class LambdaToMethod extends TreeTranslator {
88
89 private Attr attr;
90 private JCDiagnostic.Factory diags;
91 private Log log;
92 private Lower lower;
93 private Names names;
94 private Symtab syms;
95 private Resolve rs;
96 private Operators operators;
97 private TreeMaker make;
98 private Types types;
99 private TransTypes transTypes;
100 private Env<AttrContext> attrEnv;
101
102 /** the analyzer scanner */
103 private LambdaAnalyzerPreprocessor analyzer;
104
105 /** map from lambda trees to translation contexts */
106 private Map<JCTree, TranslationContext<?>> contextMap;
107
108 /** current translation context (visitor argument) */
109 private TranslationContext<?> context;
110
111 /** info about the current class being processed */
112 private KlassInfo kInfo;
113
114 /** dump statistics about lambda code generation */
115 private final boolean dumpLambdaToMethodStats;
116
117 /** force serializable representation, for stress testing **/
118 private final boolean forceSerializable;
119
120 /** true if line or local variable debug info has been requested */
121 private final boolean debugLinesOrVars;
122
123 /** dump statistics about lambda method deduplication */
124 private final boolean verboseDeduplication;
125
126 /** deduplicate lambda implementation methods */
127 private final boolean deduplicateLambdas;
128
129 /** lambda proxy is a dynamic nestmate */
130 private final boolean nestmateLambdas;
131
132 /** Flag for alternate metafactories indicating the lambda object is intended to be serializable */
133 public static final int FLAG_SERIALIZABLE = 1 << 0;
134
135 /** Flag for alternate metafactories indicating the lambda object has multiple targets */
136 public static final int FLAG_MARKERS = 1 << 1;
137
138 /** Flag for alternate metafactories indicating the lambda object requires multiple bridges */
139 public static final int FLAG_BRIDGES = 1 << 2;
140
141 // <editor-fold defaultstate="collapsed" desc="Instantiating">
142 protected static final Context.Key<LambdaToMethod> unlambdaKey = new Context.Key<>();
143
144 public static LambdaToMethod instance(Context context) {
145 LambdaToMethod instance = context.get(unlambdaKey);
146 if (instance == null) {
147 instance = new LambdaToMethod(context);
148 }
149 return instance;
150 }
151 private LambdaToMethod(Context context) {
152 context.put(unlambdaKey, this);
153 diags = JCDiagnostic.Factory.instance(context);
154 log = Log.instance(context);
155 lower = Lower.instance(context);
156 names = Names.instance(context);
157 syms = Symtab.instance(context);
158 rs = Resolve.instance(context);
159 operators = Operators.instance(context);
160 make = TreeMaker.instance(context);
161 types = Types.instance(context);
162 transTypes = TransTypes.instance(context);
163 analyzer = new LambdaAnalyzerPreprocessor();
164 Options options = Options.instance(context);
165 dumpLambdaToMethodStats = options.isSet("debug.dumpLambdaToMethodStats");
166 attr = Attr.instance(context);
167 forceSerializable = options.isSet("forceSerializable");
168 debugLinesOrVars = options.isSet(Option.G)
169 || options.isSet(Option.G_CUSTOM, "lines")
170 || options.isSet(Option.G_CUSTOM, "vars");
171 verboseDeduplication = options.isSet("debug.dumpLambdaToMethodDeduplication");
172 deduplicateLambdas = options.getBoolean("deduplicateLambdas", true);
173 nestmateLambdas = Target.instance(context).runtimeUseNestAccess();
174 }
175 // </editor-fold>
176
177 class DedupedLambda {
178 private final MethodSymbol symbol;
179 private final JCTree tree;
180
181 private int hashCode;
182
183 DedupedLambda(MethodSymbol symbol, JCTree tree) {
184 this.symbol = symbol;
185 this.tree = tree;
186 }
187
188
189 @Override
190 public int hashCode() {
191 int hashCode = this.hashCode;
192 if (hashCode == 0) {
193 this.hashCode = hashCode = TreeHasher.hash(tree, symbol.params());
194 }
195 return hashCode;
196 }
197
198 @Override
199 public boolean equals(Object o) {
200 if (!(o instanceof DedupedLambda)) {
201 return false;
202 }
203 DedupedLambda that = (DedupedLambda) o;
204 return types.isSameType(symbol.asType(), that.symbol.asType())
205 && new TreeDiffer(symbol.params(), that.symbol.params()).scan(tree, that.tree);
206 }
207 }
208
209 private class KlassInfo {
210
211 /**
212 * list of methods to append
213 */
214 private ListBuffer<JCTree> appendedMethodList;
215
216 private Map<DedupedLambda, DedupedLambda> dedupedLambdas;
217
218 private Map<Object, DynamicMethodSymbol> dynMethSyms = new HashMap<>();
219
220 /**
221 * list of deserialization cases
222 */
223 private final Map<String, ListBuffer<JCStatement>> deserializeCases;
224
225 /**
226 * deserialize method symbol
227 */
228 private final MethodSymbol deserMethodSym;
229
230 /**
231 * deserialize method parameter symbol
232 */
233 private final VarSymbol deserParamSym;
234
235 private final JCClassDecl clazz;
236
237 private KlassInfo(JCClassDecl clazz) {
238 this.clazz = clazz;
239 appendedMethodList = new ListBuffer<>();
240 dedupedLambdas = new HashMap<>();
241 deserializeCases = new HashMap<>();
242 MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType,
243 List.nil(), syms.methodClass);
244 deserMethodSym = makePrivateSyntheticMethod(STATIC, names.deserializeLambda, type, clazz.sym);
245 deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"),
246 syms.serializedLambdaType, deserMethodSym);
247 }
248
249 private void addMethod(JCTree decl) {
250 appendedMethodList = appendedMethodList.prepend(decl);
251 }
252 }
253
254 // <editor-fold defaultstate="collapsed" desc="translate methods">
255 @Override
256 public <T extends JCTree> T translate(T tree) {
257 TranslationContext<?> newContext = contextMap.get(tree);
258 return translate(tree, newContext != null ? newContext : context);
259 }
260
261 <T extends JCTree> T translate(T tree, TranslationContext<?> newContext) {
262 TranslationContext<?> prevContext = context;
263 try {
264 context = newContext;
265 return super.translate(tree);
266 }
267 finally {
268 context = prevContext;
269 }
270 }
271
272 <T extends JCTree> List<T> translate(List<T> trees, TranslationContext<?> newContext) {
273 ListBuffer<T> buf = new ListBuffer<>();
274 for (T tree : trees) {
275 buf.append(translate(tree, newContext));
276 }
277 return buf.toList();
278 }
279
280 public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
281 this.make = make;
282 this.attrEnv = env;
283 this.context = null;
284 this.contextMap = new HashMap<>();
285 return translate(cdef);
286 }
287 // </editor-fold>
288
289 // <editor-fold defaultstate="collapsed" desc="visitor methods">
290 /**
291 * Visit a class.
292 * Maintain the translatedMethodList across nested classes.
293 * Append the translatedMethodList to the class after it is translated.
294 * @param tree
295 */
296 @Override
297 public void visitClassDef(JCClassDecl tree) {
298 if (tree.sym.owner.kind == PCK) {
299 //analyze class
300 tree = analyzer.analyzeAndPreprocessClass(tree);
301 }
302 KlassInfo prevKlassInfo = kInfo;
303 try {
304 kInfo = new KlassInfo(tree);
305 super.visitClassDef(tree);
306 if (!kInfo.deserializeCases.isEmpty()) {
307 int prevPos = make.pos;
308 try {
309 make.at(tree);
310 kInfo.addMethod(makeDeserializeMethod(tree.sym));
311 } finally {
312 make.at(prevPos);
313 }
314 }
315 //add all translated instance methods here
316 List<JCTree> newMethods = kInfo.appendedMethodList.toList();
317 tree.defs = tree.defs.appendList(newMethods);
318 for (JCTree lambda : newMethods) {
319 tree.sym.members().enter(((JCMethodDecl)lambda).sym);
320 }
321 result = tree;
322 } finally {
323 kInfo = prevKlassInfo;
324 }
325 }
326
327 /**
328 * Translate a lambda into a method to be inserted into the class.
329 * Then replace the lambda site with an invokedynamic call of to lambda
330 * meta-factory, which will use the lambda method.
331 * @param tree
332 */
333 @Override
334 public void visitLambda(JCLambda tree) {
335 LambdaTranslationContext localContext = (LambdaTranslationContext)context;
336 MethodSymbol sym = localContext.translatedSym;
337 MethodType lambdaType = (MethodType) sym.type;
338
339 { /* Type annotation management: Based on where the lambda features, type annotations that
340 are interior to it, may at this point be attached to the enclosing method, or the first
341 constructor in the class, or in the enclosing class symbol or in the field whose
342 initializer is the lambda. In any event, gather up the annotations that belong to the
343 lambda and attach it to the implementation method.
344 */
345
346 Symbol owner = localContext.owner;
347 apportionTypeAnnotations(tree,
348 owner::getRawTypeAttributes,
349 owner::setTypeAttributes,
350 sym::setTypeAttributes);
351
352
353 boolean init;
354 if ((init = (owner.name == names.init)) || owner.name == names.clinit) {
355 owner = owner.owner;
356 apportionTypeAnnotations(tree,
357 init ? owner::getInitTypeAttributes : owner::getClassInitTypeAttributes,
358 init ? owner::setInitTypeAttributes : owner::setClassInitTypeAttributes,
359 sym::appendUniqueTypeAttributes);
360 }
361 if (localContext.self != null && localContext.self.getKind() == ElementKind.FIELD) {
362 owner = localContext.self;
363 apportionTypeAnnotations(tree,
364 owner::getRawTypeAttributes,
365 owner::setTypeAttributes,
366 sym::appendUniqueTypeAttributes);
367 }
368 }
369
370 //create the method declaration hoisting the lambda body
371 JCMethodDecl lambdaDecl = make.MethodDef(make.Modifiers(sym.flags_field),
372 sym.name,
373 make.QualIdent(lambdaType.getReturnType().tsym),
374 List.nil(),
375 localContext.syntheticParams,
376 lambdaType.getThrownTypes() == null ?
377 List.nil() :
378 make.Types(lambdaType.getThrownTypes()),
379 null,
380 null);
381 lambdaDecl.sym = sym;
382 lambdaDecl.type = lambdaType;
383
384 //translate lambda body
385 //As the lambda body is translated, all references to lambda locals,
386 //captured variables, enclosing members are adjusted accordingly
387 //to refer to the static method parameters (rather than i.e. accessing
388 //captured members directly).
389 lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl));
390
391 boolean dedupe = false;
392 if (deduplicateLambdas && !debugLinesOrVars && !localContext.isSerializable()) {
393 DedupedLambda dedupedLambda = new DedupedLambda(lambdaDecl.sym, lambdaDecl.body);
394 DedupedLambda existing = kInfo.dedupedLambdas.putIfAbsent(dedupedLambda, dedupedLambda);
395 if (existing != null) {
396 sym = existing.symbol;
397 dedupe = true;
398 if (verboseDeduplication) log.note(tree, Notes.VerboseL2mDeduplicate(sym));
399 }
400 }
401 if (!dedupe) {
402 //Add the method to the list of methods to be added to this class.
403 kInfo.addMethod(lambdaDecl);
404 }
405
406 //now that we have generated a method for the lambda expression,
407 //we can translate the lambda into a method reference pointing to the newly
408 //created method.
409 //
410 //Note that we need to adjust the method handle so that it will match the
411 //signature of the SAM descriptor - this means that the method reference
412 //should be added the following synthetic arguments:
413 //
414 // * the "this" argument if it is an instance method
415 // * enclosing locals captured by the lambda expression
416
417 ListBuffer<JCExpression> syntheticInits = new ListBuffer<>();
418
419 if (localContext.methodReferenceReceiver != null) {
420 syntheticInits.append(localContext.methodReferenceReceiver);
421 } else if (!sym.isStatic()) {
422 syntheticInits.append(makeThis(
423 sym.owner.enclClass().asType(),
424 localContext.owner.enclClass()));
425 }
426
427 //add captured locals
428 for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) {
429 if (fv != localContext.self) {
430 JCTree captured_local = make.Ident(fv).setType(fv.type);
431 syntheticInits.append((JCExpression) captured_local);
432 }
433 }
434 // add captured outer this instances (used only when `this' capture itself is illegal)
435 for (Symbol fv : localContext.getSymbolMap(CAPTURED_OUTER_THIS).keySet()) {
436 JCTree captured_local = make.QualThis(fv.type);
437 syntheticInits.append((JCExpression) captured_local);
438 }
439
440 //then, determine the arguments to the indy call
441 List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev);
442
443 //convert to an invokedynamic call
444 result = makeMetafactoryIndyCall(context, sym.asHandle(), indy_args);
445 }
446
447 // where
448 // Reassign type annotations from the source that should really belong to the lambda
449 private void apportionTypeAnnotations(JCLambda tree,
450 Supplier<List<Attribute.TypeCompound>> source,
451 Consumer<List<Attribute.TypeCompound>> owner,
452 Consumer<List<Attribute.TypeCompound>> lambda) {
453
454 ListBuffer<Attribute.TypeCompound> ownerTypeAnnos = new ListBuffer<>();
455 ListBuffer<Attribute.TypeCompound> lambdaTypeAnnos = new ListBuffer<>();
456
457 for (Attribute.TypeCompound tc : source.get()) {
458 if (tc.position.onLambda == tree) {
459 lambdaTypeAnnos.append(tc);
460 } else {
461 ownerTypeAnnos.append(tc);
462 }
463 }
464 if (lambdaTypeAnnos.nonEmpty()) {
465 owner.accept(ownerTypeAnnos.toList());
466 lambda.accept(lambdaTypeAnnos.toList());
467 }
468 }
469
470 private JCIdent makeThis(Type type, Symbol owner) {
471 VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC,
472 names._this,
473 type,
474 owner);
475 return make.Ident(_this);
476 }
477
478 /**
479 * Translate a method reference into an invokedynamic call to the
480 * meta-factory.
481 * @param tree
482 */
483 @Override
484 public void visitReference(JCMemberReference tree) {
485 ReferenceTranslationContext localContext = (ReferenceTranslationContext)context;
486
487 //first determine the method symbol to be used to generate the sam instance
488 //this is either the method reference symbol, or the bridged reference symbol
489 MethodSymbol refSym = (MethodSymbol)tree.sym;
490
491 //the qualifying expression is treated as a special captured arg
492 JCExpression init;
493 switch(tree.kind) {
494
495 case IMPLICIT_INNER: /** Inner :: new */
496 case SUPER: /** super :: instMethod */
497 init = makeThis(
498 localContext.owner.enclClass().asType(),
499 localContext.owner.enclClass());
500 break;
501
502 case BOUND: /** Expr :: instMethod */
503 init = transTypes.coerce(attrEnv, tree.getQualifierExpression(),
504 types.erasure(tree.sym.owner.type));
505 init = attr.makeNullCheck(init);
506 break;
507
508 case UNBOUND: /** Type :: instMethod */
509 case STATIC: /** Type :: staticMethod */
510 case TOPLEVEL: /** Top level :: new */
511 case ARRAY_CTOR: /** ArrayType :: new */
512 init = null;
513 break;
514
515 default:
516 throw new InternalError("Should not have an invalid kind");
517 }
518
519 List<JCExpression> indy_args = init==null? List.nil() : translate(List.of(init), localContext.prev);
520
521
522 //build a sam instance using an indy call to the meta-factory
523 result = makeMetafactoryIndyCall(localContext, refSym.asHandle(), indy_args);
524 }
525
526 /**
527 * Translate identifiers within a lambda to the mapped identifier
528 * @param tree
529 */
530 @Override
531 public void visitIdent(JCIdent tree) {
532 if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) {
533 super.visitIdent(tree);
534 } else {
535 int prevPos = make.pos;
536 try {
537 make.at(tree);
538
539 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context;
540 JCTree ltree = lambdaContext.translate(tree);
541 if (ltree != null) {
542 result = ltree;
543 } else {
544 //access to untranslated symbols (i.e. compile-time constants,
545 //members defined inside the lambda body, etc.) )
546 super.visitIdent(tree);
547 }
548 } finally {
549 make.at(prevPos);
550 }
551 }
552 }
553
554 /**
555 * Translate qualified `this' references within a lambda to the mapped identifier
556 * @param tree
557 */
558 @Override
559 public void visitSelect(JCFieldAccess tree) {
560 if (context == null || !analyzer.lambdaFieldAccessFilter(tree)) {
561 super.visitSelect(tree);
562 } else {
563 int prevPos = make.pos;
564 try {
565 make.at(tree);
566
567 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context;
568 JCTree ltree = lambdaContext.translate(tree);
569 if (ltree != null) {
570 result = ltree;
571 } else {
572 super.visitSelect(tree);
573 }
574 } finally {
575 make.at(prevPos);
576 }
577 }
578 }
579
580 /**
581 * Translate instance creation expressions with implicit enclosing instances
582 * @param tree
583 */
584 @Override
585 public void visitNewClass(JCNewClass tree) {
586 if (context == null || !analyzer.lambdaNewClassFilter(context, tree)) {
587 super.visitNewClass(tree);
588 } else {
589 int prevPos = make.pos;
590 try {
591 make.at(tree);
592
593 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context;
594 tree = lambdaContext.translate(tree);
595 super.visitNewClass(tree);
596 } finally {
597 make.at(prevPos);
598 }
599 }
600 }
601
602 @Override
603 public void visitVarDef(JCVariableDecl tree) {
604 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context;
605 if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
606 tree.init = translate(tree.init);
607 tree.sym = (VarSymbol) lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym);
608 result = tree;
609 } else {
610 super.visitVarDef(tree);
611 }
612 }
613
614 // </editor-fold>
615
616 // <editor-fold defaultstate="collapsed" desc="Translation helper methods">
617
618 private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) {
619 return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ?
620 makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) :
621 makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally);
622 }
623
624 private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) {
625 Type restype = lambdaMethodDecl.type.getReturnType();
626 boolean isLambda_void = expr.type.hasTag(VOID);
627 boolean isTarget_void = restype.hasTag(VOID);
628 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
629 int prevPos = make.pos;
630 try {
631 if (isTarget_void) {
632 //target is void:
633 // BODY;
634 JCStatement stat = make.at(expr).Exec(expr);
635 return make.Block(0, List.of(stat));
636 } else if (isLambda_void && isTarget_Void) {
637 //void to Void conversion:
638 // BODY; return null;
639 ListBuffer<JCStatement> stats = new ListBuffer<>();
640 stats.append(make.at(expr).Exec(expr));
641 stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
642 return make.Block(0, stats.toList());
643 } else {
644 //non-void to non-void conversion:
645 // return BODY;
646 return make.at(expr).Block(0, List.of(make.Return(expr)));
647 }
648 } finally {
649 make.at(prevPos);
650 }
651 }
652
653 private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) {
654 final Type restype = lambdaMethodDecl.type.getReturnType();
655 final boolean isTarget_void = restype.hasTag(VOID);
656 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
657
658 class LambdaBodyTranslator extends TreeTranslator {
659
660 @Override
661 public void visitClassDef(JCClassDecl tree) {
662 //do NOT recurse on any inner classes
663 result = tree;
664 }
665
666 @Override
667 public void visitLambda(JCLambda tree) {
668 //do NOT recurse on any nested lambdas
669 result = tree;
670 }
671
672 @Override
673 public void visitReturn(JCReturn tree) {
674 boolean isLambda_void = tree.expr == null;
675 if (isTarget_void && !isLambda_void) {
676 //Void to void conversion:
677 // { TYPE $loc = RET-EXPR; return; }
678 VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym);
679 JCVariableDecl varDef = make.VarDef(loc, tree.expr);
680 result = make.Block(0, List.of(varDef, make.Return(null)));
681 } else {
682 result = tree;
683 }
684
685 }
686 }
687
688 JCBlock trans_block = new LambdaBodyTranslator().translate(block);
689 if (completeNormally && isTarget_Void) {
690 //there's no return statement and the lambda (possibly inferred)
691 //return type is java.lang.Void; emit a synthetic return statement
692 trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
693 }
694 return trans_block;
695 }
696
697 private JCMethodDecl makeDeserializeMethod(Symbol kSym) {
698 ListBuffer<JCCase> cases = new ListBuffer<>();
699 ListBuffer<JCBreak> breaks = new ListBuffer<>();
700 for (Map.Entry<String, ListBuffer<JCStatement>> entry : kInfo.deserializeCases.entrySet()) {
701 JCBreak br = make.Break(null);
702 breaks.add(br);
703 List<JCStatement> stmts = entry.getValue().append(br).toList();
704 cases.add(make.Case(JCCase.STATEMENT, List.of(make.Literal(entry.getKey())), stmts, null));
705 }
706 JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
707 for (JCBreak br : breaks) {
708 br.target = sw;
709 }
710 JCBlock body = make.Block(0L, List.of(
711 sw,
712 make.Throw(makeNewClass(
713 syms.illegalArgumentExceptionType,
714 List.of(make.Literal("Invalid lambda deserialization"))))));
715 JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()),
716 names.deserializeLambda,
717 make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym),
718 List.nil(),
719 List.of(make.VarDef(kInfo.deserParamSym, null)),
720 List.nil(),
721 body,
722 null);
723 deser.sym = kInfo.deserMethodSym;
724 deser.type = kInfo.deserMethodSym.type;
725 //System.err.printf("DESER: '%s'\n", deser);
726 return deser;
727 }
728
729 /** Make an attributed class instance creation expression.
730 * @param ctype The class type.
731 * @param args The constructor arguments.
732 * @param cons The constructor symbol
733 */
734 JCNewClass makeNewClass(Type ctype, List<JCExpression> args, Symbol cons) {
735 JCNewClass tree = make.NewClass(null,
736 null, make.QualIdent(ctype.tsym), args, null);
737 tree.constructor = cons;
738 tree.type = ctype;
739 return tree;
740 }
741
742 /** Make an attributed class instance creation expression.
743 * @param ctype The class type.
744 * @param args The constructor arguments.
745 */
746 JCNewClass makeNewClass(Type ctype, List<JCExpression> args) {
747 return makeNewClass(ctype, args,
748 rs.resolveConstructor(null, attrEnv, ctype, TreeInfo.types(args), List.nil()));
749 }
750
751 private void addDeserializationCase(MethodHandleSymbol refSym, Type targetType, MethodSymbol samSym,
752 DiagnosticPosition pos, List<LoadableConstant> staticArgs, MethodType indyType) {
753 String functionalInterfaceClass = classSig(targetType);
754 String functionalInterfaceMethodName = samSym.getSimpleName().toString();
755 String functionalInterfaceMethodSignature = typeSig(types.erasure(samSym.type));
756 String implClass = classSig(types.erasure(refSym.owner.type));
757 String implMethodName = refSym.getQualifiedName().toString();
758 String implMethodSignature = typeSig(types.erasure(refSym.type));
759
760 JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType),
761 make.Literal(refSym.referenceKind()));
762 ListBuffer<JCExpression> serArgs = new ListBuffer<>();
763 int i = 0;
764 for (Type t : indyType.getParameterTypes()) {
765 List<JCExpression> indexAsArg = new ListBuffer<JCExpression>().append(make.Literal(i)).toList();
766 List<Type> argTypes = new ListBuffer<Type>().append(syms.intType).toList();
767 serArgs.add(make.TypeCast(types.erasure(t), deserGetter("getCapturedArg", syms.objectType, argTypes, indexAsArg)));
768 ++i;
769 }
770 JCStatement stmt = make.If(
771 deserTest(deserTest(deserTest(deserTest(deserTest(
772 kindTest,
773 "getFunctionalInterfaceClass", functionalInterfaceClass),
774 "getFunctionalInterfaceMethodName", functionalInterfaceMethodName),
775 "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature),
776 "getImplClass", implClass),
777 "getImplMethodSignature", implMethodSignature),
778 make.Return(makeIndyCall(
779 pos,
780 syms.lambdaMetafactory,
781 names.altMetafactory,
782 staticArgs, indyType, serArgs.toList(), samSym.name)),
783 null);
784 ListBuffer<JCStatement> stmts = kInfo.deserializeCases.get(implMethodName);
785 if (stmts == null) {
786 stmts = new ListBuffer<>();
787 kInfo.deserializeCases.put(implMethodName, stmts);
788 }
789 /****
790 System.err.printf("+++++++++++++++++\n");
791 System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass);
792 System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName);
793 System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature);
794 System.err.printf("*implMethodKind: %d\n", implMethodKind);
795 System.err.printf("*implClass: '%s'\n", implClass);
796 System.err.printf("*implMethodName: '%s'\n", implMethodName);
797 System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature);
798 ****/
799 stmts.append(stmt);
800 }
801
802 private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) {
803 JCBinary testExpr = make.Binary(JCTree.Tag.EQ, arg1, arg2);
804 testExpr.operator = operators.resolveBinary(testExpr, JCTree.Tag.EQ, argType, argType);
805 testExpr.setType(syms.booleanType);
806 return testExpr;
807 }
808
809 private JCExpression deserTest(JCExpression prev, String func, String lit) {
810 MethodType eqmt = new MethodType(List.of(syms.objectType), syms.booleanType, List.nil(), syms.methodClass);
811 Symbol eqsym = rs.resolveQualifiedMethod(null, attrEnv, syms.objectType, names.equals, List.of(syms.objectType), List.nil());
812 JCMethodInvocation eqtest = make.Apply(
813 List.nil(),
814 make.Select(deserGetter(func, syms.stringType), eqsym).setType(eqmt),
815 List.of(make.Literal(lit)));
816 eqtest.setType(syms.booleanType);
817 JCBinary compound = make.Binary(JCTree.Tag.AND, prev, eqtest);
818 compound.operator = operators.resolveBinary(compound, JCTree.Tag.AND, syms.booleanType, syms.booleanType);
819 compound.setType(syms.booleanType);
820 return compound;
821 }
822
823 private JCExpression deserGetter(String func, Type type) {
824 return deserGetter(func, type, List.nil(), List.nil());
825 }
826
827 private JCExpression deserGetter(String func, Type type, List<Type> argTypes, List<JCExpression> args) {
828 MethodType getmt = new MethodType(argTypes, type, List.nil(), syms.methodClass);
829 Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.nil());
830 return make.Apply(
831 List.nil(),
832 make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt),
833 args).setType(type);
834 }
835
836 /**
837 * Create new synthetic method with given flags, name, type, owner
838 */
839 private MethodSymbol makePrivateSyntheticMethod(long flags, Name name, Type type, Symbol owner) {
840 return new MethodSymbol(flags | SYNTHETIC | PRIVATE, name, type, owner);
841 }
842
843 /**
844 * Create new synthetic variable with given flags, name, type, owner
845 */
846 private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) {
847 return new VarSymbol(flags | SYNTHETIC, name, type, owner);
848 }
849
850 /**
851 * Set varargsElement field on a given tree (must be either a new class tree
852 * or a method call tree)
853 */
854 private void setVarargsIfNeeded(JCTree tree, Type varargsElement) {
855 if (varargsElement != null) {
856 switch (tree.getTag()) {
857 case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break;
858 case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break;
859 case TYPECAST: setVarargsIfNeeded(((JCTypeCast) tree).expr, varargsElement); break;
860 default: throw new AssertionError();
861 }
862 }
863 }
864
865 /**
866 * Convert method/constructor arguments by inserting appropriate cast
867 * as required by type-erasure - this is needed when bridging a lambda/method
868 * reference, as the bridged signature might require downcast to be compatible
869 * with the generated signature.
870 */
871 private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) {
872 Assert.check(meth.kind == MTH);
873 List<Type> formals = types.erasure(meth.type).getParameterTypes();
874 if (varargsElement != null) {
875 Assert.check((meth.flags() & VARARGS) != 0);
876 }
877 return transTypes.translateArgs(args, formals, varargsElement, attrEnv);
878 }
879
880 // </editor-fold>
881
882 /**
883 * Converts a method reference which cannot be used directly into a lambda
884 */
885 private class MemberReferenceToLambda {
886
887 private final JCMemberReference tree;
888 private final ReferenceTranslationContext localContext;
889 private final Symbol owner;
890 private final ListBuffer<JCExpression> args = new ListBuffer<>();
891 private final ListBuffer<JCVariableDecl> params = new ListBuffer<>();
892
893 private JCExpression receiverExpression = null;
894
895 MemberReferenceToLambda(JCMemberReference tree, ReferenceTranslationContext localContext, Symbol owner) {
896 this.tree = tree;
897 this.localContext = localContext;
898 this.owner = owner;
899 }
900
901 JCLambda lambda() {
902 int prevPos = make.pos;
903 try {
904 make.at(tree);
905
906 //body generation - this can be either a method call or a
907 //new instance creation expression, depending on the member reference kind
908 VarSymbol rcvr = addParametersReturnReceiver();
909 JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE)
910 ? expressionInvoke(rcvr)
911 : expressionNew();
912
913 JCLambda slam = make.Lambda(params.toList(), expr);
914 slam.target = tree.target;
915 slam.type = tree.type;
916 slam.pos = tree.pos;
917 return slam;
918 } finally {
919 make.at(prevPos);
920 }
921 }
922
923 /**
924 * Generate the parameter list for the converted member reference.
925 *
926 * @return The receiver variable symbol, if any
927 */
928 VarSymbol addParametersReturnReceiver() {
929 Type samDesc = localContext.bridgedRefSig();
930 List<Type> samPTypes = samDesc.getParameterTypes();
931 List<Type> descPTypes = tree.getDescriptorType(types).getParameterTypes();
932
933 // Determine the receiver, if any
934 VarSymbol rcvr;
935 switch (tree.kind) {
936 case BOUND:
937 // The receiver is explicit in the method reference
938 rcvr = addParameter("rec$", tree.getQualifierExpression().type, false);
939 receiverExpression = attr.makeNullCheck(tree.getQualifierExpression());
940 break;
941 case UNBOUND:
942 // The receiver is the first parameter, extract it and
943 // adjust the SAM and unerased type lists accordingly
944 rcvr = addParameter("rec$", samDesc.getParameterTypes().head, false);
945 samPTypes = samPTypes.tail;
946 descPTypes = descPTypes.tail;
947 break;
948 default:
949 rcvr = null;
950 break;
951 }
952 List<Type> implPTypes = tree.sym.type.getParameterTypes();
953 int implSize = implPTypes.size();
954 int samSize = samPTypes.size();
955 // Last parameter to copy from referenced method, exclude final var args
956 int last = localContext.needsVarArgsConversion() ? implSize - 1 : implSize;
957
958 // Failsafe -- assure match-up
959 boolean checkForIntersection = tree.varargsElement != null || implSize == descPTypes.size();
960
961 // Use parameter types of the implementation method unless the unerased
962 // SAM parameter type is an intersection type, in that case use the
963 // erased SAM parameter type so that the supertype relationship
964 // the implementation method parameters is not obscured.
965 // Note: in this loop, the lists implPTypes, samPTypes, and descPTypes
966 // are used as pointers to the current parameter type information
967 // and are thus not usable afterwards.
968 for (int i = 0; implPTypes.nonEmpty() && i < last; ++i) {
969 // By default use the implementation method parameter type
970 Type parmType = implPTypes.head;
971 // If the unerased parameter type is a type variable whose
972 // bound is an intersection (eg. <T extends A & B>) then
973 // use the SAM parameter type
974 if (checkForIntersection && descPTypes.head.getKind() == TypeKind.TYPEVAR) {
975 TypeVar tv = (TypeVar) descPTypes.head;
976 if (tv.getUpperBound().getKind() == TypeKind.INTERSECTION) {
977 parmType = samPTypes.head;
978 }
979 }
980 addParameter("x$" + i, parmType, true);
981
982 // Advance to the next parameter
983 implPTypes = implPTypes.tail;
984 samPTypes = samPTypes.tail;
985 descPTypes = descPTypes.tail;
986 }
987 // Flatten out the var args
988 for (int i = last; i < samSize; ++i) {
989 addParameter("xva$" + i, tree.varargsElement, true);
990 }
991
992 return rcvr;
993 }
994
995 JCExpression getReceiverExpression() {
996 return receiverExpression;
997 }
998
999 private JCExpression makeReceiver(VarSymbol rcvr) {
1000 if (rcvr == null) return null;
1001 JCExpression rcvrExpr = make.Ident(rcvr);
1002 boolean protAccess =
1003 isProtectedInSuperClassOfEnclosingClassInOtherPackage(tree.sym, owner);
1004 Type rcvrType = tree.ownerAccessible && !protAccess ? tree.sym.enclClass().type
1005 : tree.expr.type;
1006 if (rcvrType == syms.arrayClass.type) {
1007 // Map the receiver type to the actually type, not just "array"
1008 rcvrType = tree.getQualifierExpression().type;
1009 }
1010 if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) {
1011 rcvrExpr = make.TypeCast(make.Type(rcvrType), rcvrExpr).setType(rcvrType);
1012 }
1013 return rcvrExpr;
1014 }
1015
1016 /**
1017 * determine the receiver of the method call - the receiver can
1018 * be a type qualifier, the synthetic receiver parameter or 'super'.
1019 */
1020 private JCExpression expressionInvoke(VarSymbol rcvr) {
1021 JCExpression qualifier =
1022 (rcvr != null) ?
1023 makeReceiver(rcvr) :
1024 tree.getQualifierExpression();
1025
1026 //create the qualifier expression
1027 JCFieldAccess select = make.Select(qualifier, tree.sym.name);
1028 select.sym = tree.sym;
1029 select.type = tree.sym.erasure(types);
1030
1031 //create the method call expression
1032 JCExpression apply = make.Apply(List.nil(), select,
1033 convertArgs(tree.sym, args.toList(), tree.varargsElement)).
1034 setType(tree.sym.erasure(types).getReturnType());
1035
1036 apply = transTypes.coerce(attrEnv, apply,
1037 types.erasure(localContext.tree.referentType.getReturnType()));
1038
1039 setVarargsIfNeeded(apply, tree.varargsElement);
1040 return apply;
1041 }
1042
1043 /**
1044 * Lambda body to use for a 'new'.
1045 */
1046 private JCExpression expressionNew() {
1047 if (tree.kind == ReferenceKind.ARRAY_CTOR) {
1048 //create the array creation expression
1049 JCNewArray newArr = make.NewArray(
1050 make.Type(types.elemtype(tree.getQualifierExpression().type)),
1051 List.of(make.Ident(params.first())),
1052 null);
1053 newArr.type = tree.getQualifierExpression().type;
1054 return newArr;
1055 } else {
1056 //create the instance creation expression
1057 //note that method reference syntax does not allow an explicit
1058 //enclosing class (so the enclosing class is null)
1059 // but this may need to be patched up later with the proxy for the outer this
1060 JCNewClass newClass = make.NewClass(null,
1061 List.nil(),
1062 make.Type(tree.getQualifierExpression().type),
1063 convertArgs(tree.sym, args.toList(), tree.varargsElement),
1064 null);
1065 newClass.constructor = tree.sym;
1066 newClass.constructorType = tree.sym.erasure(types);
1067 newClass.type = tree.getQualifierExpression().type;
1068 setVarargsIfNeeded(newClass, tree.varargsElement);
1069 return newClass;
1070 }
1071 }
1072
1073 private VarSymbol addParameter(String name, Type p, boolean genArg) {
1074 VarSymbol vsym = new VarSymbol(PARAMETER | SYNTHETIC, names.fromString(name), p, owner);
1075 vsym.pos = tree.pos;
1076 params.append(make.VarDef(vsym, null));
1077 if (genArg) {
1078 args.append(make.Ident(vsym));
1079 }
1080 return vsym;
1081 }
1082 }
1083
1084 private MethodType typeToMethodType(Type mt) {
1085 Type type = types.erasure(mt);
1086 return new MethodType(type.getParameterTypes(),
1087 type.getReturnType(),
1088 type.getThrownTypes(),
1089 syms.methodClass);
1090 }
1091
1092 /**
1093 * Generate an indy method call to the meta factory
1094 */
1095 private JCExpression makeMetafactoryIndyCall(TranslationContext<?> context,
1096 MethodHandleSymbol refSym, List<JCExpression> indy_args) {
1097 JCFunctionalExpression tree = context.tree;
1098 //determine the static bsm args
1099 MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.target.tsym);
1100 List<LoadableConstant> staticArgs = List.of(
1101 typeToMethodType(samSym.type),
1102 refSym.asHandle(),
1103 typeToMethodType(tree.getDescriptorType(types)));
1104
1105 //computed indy arg types
1106 ListBuffer<Type> indy_args_types = new ListBuffer<>();
1107 for (JCExpression arg : indy_args) {
1108 indy_args_types.append(arg.type);
1109 }
1110
1111 //finally, compute the type of the indy call
1112 MethodType indyType = new MethodType(indy_args_types.toList(),
1113 tree.type,
1114 List.nil(),
1115 syms.methodClass);
1116
1117 Name metafactoryName = context.needsAltMetafactory() ?
1118 names.altMetafactory : names.metafactory;
1119
1120 if (context.needsAltMetafactory()) {
1121 ListBuffer<Type> markers = new ListBuffer<>();
1122 List<Type> targets = tree.target.isIntersection() ?
1123 types.directSupertypes(tree.target) :
1124 List.nil();
1125 for (Type t : targets) {
1126 t = types.erasure(t);
1127 if (t.tsym != syms.serializableType.tsym &&
1128 t.tsym != tree.type.tsym &&
1129 t.tsym != syms.objectType.tsym) {
1130 markers.append(t);
1131 }
1132 }
1133 int flags = context.isSerializable() ? FLAG_SERIALIZABLE : 0;
1134 boolean hasMarkers = markers.nonEmpty();
1135 boolean hasBridges = context.bridges.nonEmpty();
1136 if (hasMarkers) {
1137 flags |= FLAG_MARKERS;
1138 }
1139 if (hasBridges) {
1140 flags |= FLAG_BRIDGES;
1141 }
1142 staticArgs = staticArgs.append(LoadableConstant.Int(flags));
1143 if (hasMarkers) {
1144 staticArgs = staticArgs.append(LoadableConstant.Int(markers.length()));
1145 staticArgs = staticArgs.appendList(List.convert(LoadableConstant.class, markers.toList()));
1146 }
1147 if (hasBridges) {
1148 staticArgs = staticArgs.append(LoadableConstant.Int(context.bridges.length() - 1));
1149 for (Symbol s : context.bridges) {
1150 Type s_erasure = s.erasure(types);
1151 if (!types.isSameType(s_erasure, samSym.erasure(types))) {
1152 staticArgs = staticArgs.append(((MethodType)s.erasure(types)));
1153 }
1154 }
1155 }
1156 if (context.isSerializable()) {
1157 int prevPos = make.pos;
1158 try {
1159 make.at(kInfo.clazz);
1160 addDeserializationCase(refSym, tree.type, samSym,
1161 tree, staticArgs, indyType);
1162 } finally {
1163 make.at(prevPos);
1164 }
1165 }
1166 }
1167
1168 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args, samSym.name);
1169 }
1170
1171 /**
1172 * Generate an indy method call with given name, type and static bootstrap
1173 * arguments types
1174 */
1175 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
1176 List<LoadableConstant> staticArgs, MethodType indyType, List<JCExpression> indyArgs,
1177 Name methName) {
1178 int prevPos = make.pos;
1179 try {
1180 make.at(pos);
1181 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
1182 syms.stringType,
1183 syms.methodTypeType).appendList(staticArgs.map(types::constantType));
1184
1185 Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site,
1186 bsmName, bsm_staticArgs, List.nil());
1187
1188 DynamicMethodSymbol dynSym =
1189 new DynamicMethodSymbol(methName,
1190 syms.noSymbol,
1191 ((MethodSymbol)bsm).asHandle(),
1192 indyType,
1193 staticArgs.toArray(new LoadableConstant[staticArgs.length()]));
1194 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName);
1195 DynamicMethodSymbol existing = kInfo.dynMethSyms.putIfAbsent(
1196 dynSym.poolKey(types), dynSym);
1197 qualifier.sym = existing != null ? existing : dynSym;
1198 qualifier.type = indyType.getReturnType();
1199
1200 JCMethodInvocation proxyCall = make.Apply(List.nil(), qualifier, indyArgs);
1201 proxyCall.type = indyType.getReturnType();
1202 return proxyCall;
1203 } finally {
1204 make.at(prevPos);
1205 }
1206 }
1207
1208 // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer">
1209 /**
1210 * This visitor collects information about translation of a lambda expression.
1211 * More specifically, it keeps track of the enclosing contexts and captured locals
1212 * accessed by the lambda being translated (as well as other useful info).
1213 * It also translates away problems for LambdaToMethod.
1214 */
1215 class LambdaAnalyzerPreprocessor extends TreeTranslator {
1216
1217 /** the frame stack - used to reconstruct translation info about enclosing scopes */
1218 private List<Frame> frameStack;
1219
1220 /**
1221 * keep the count of lambda expression (used to generate unambiguous
1222 * names)
1223 */
1224 private int lambdaCount = 0;
1225
1226 /**
1227 * List of types undergoing construction via explicit constructor chaining.
1228 */
1229 private List<ClassSymbol> typesUnderConstruction;
1230
1231 /**
1232 * keep the count of lambda expression defined in given context (used to
1233 * generate unambiguous names for serializable lambdas)
1234 */
1235 private class SyntheticMethodNameCounter {
1236 private Map<String, Integer> map = new HashMap<>();
1237 int getIndex(StringBuilder buf) {
1238 String temp = buf.toString();
1239 Integer count = map.get(temp);
1240 if (count == null) {
1241 count = 0;
1242 }
1243 ++count;
1244 map.put(temp, count);
1245 return count;
1246 }
1247 }
1248 private SyntheticMethodNameCounter syntheticMethodNameCounts =
1249 new SyntheticMethodNameCounter();
1250
1251 private Map<Symbol, JCClassDecl> localClassDefs;
1252
1253 /**
1254 * maps for fake clinit symbols to be used as owners of lambda occurring in
1255 * a static var init context
1256 */
1257 private Map<ClassSymbol, Symbol> clinits = new HashMap<>();
1258
1259 private JCClassDecl analyzeAndPreprocessClass(JCClassDecl tree) {
1260 frameStack = List.nil();
1261 typesUnderConstruction = List.nil();
1262 localClassDefs = new HashMap<>();
1263 return translate(tree);
1264 }
1265
1266 @Override
1267 public void visitApply(JCMethodInvocation tree) {
1268 List<ClassSymbol> previousNascentTypes = typesUnderConstruction;
1269 try {
1270 Name methName = TreeInfo.name(tree.meth);
1271 if (methName == names._this || methName == names._super) {
1272 typesUnderConstruction = typesUnderConstruction.prepend(currentClass());
1273 }
1274 super.visitApply(tree);
1275 } finally {
1276 typesUnderConstruction = previousNascentTypes;
1277 }
1278 }
1279 // where
1280 private ClassSymbol currentClass() {
1281 for (Frame frame : frameStack) {
1282 if (frame.tree.hasTag(JCTree.Tag.CLASSDEF)) {
1283 JCClassDecl cdef = (JCClassDecl) frame.tree;
1284 return cdef.sym;
1285 }
1286 }
1287 return null;
1288 }
1289
1290 @Override
1291 public void visitBlock(JCBlock tree) {
1292 List<Frame> prevStack = frameStack;
1293 try {
1294 if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) {
1295 frameStack = frameStack.prepend(new Frame(tree));
1296 }
1297 super.visitBlock(tree);
1298 }
1299 finally {
1300 frameStack = prevStack;
1301 }
1302 }
1303
1304 @Override
1305 public void visitClassDef(JCClassDecl tree) {
1306 List<Frame> prevStack = frameStack;
1307 int prevLambdaCount = lambdaCount;
1308 SyntheticMethodNameCounter prevSyntheticMethodNameCounts =
1309 syntheticMethodNameCounts;
1310 Map<ClassSymbol, Symbol> prevClinits = clinits;
1311 DiagnosticSource prevSource = log.currentSource();
1312 try {
1313 log.useSource(tree.sym.sourcefile);
1314 lambdaCount = 0;
1315 syntheticMethodNameCounts = new SyntheticMethodNameCounter();
1316 prevClinits = new HashMap<>();
1317 if (tree.sym.owner.kind == MTH) {
1318 localClassDefs.put(tree.sym, tree);
1319 }
1320 if (directlyEnclosingLambda() != null) {
1321 tree.sym.owner = owner();
1322 if (tree.sym.hasOuterInstance()) {
1323 //if a class is defined within a lambda, the lambda must capture
1324 //its enclosing instance (if any)
1325 TranslationContext<?> localContext = context();
1326 final TypeSymbol outerInstanceSymbol = tree.sym.type.getEnclosingType().tsym;
1327 while (localContext != null && !localContext.owner.isStatic()) {
1328 if (localContext.tree.hasTag(LAMBDA)) {
1329 JCTree block = capturedDecl(localContext.depth, outerInstanceSymbol);
1330 if (block == null) break;
1331 ((LambdaTranslationContext)localContext)
1332 .addSymbol(outerInstanceSymbol, CAPTURED_THIS);
1333 }
1334 localContext = localContext.prev;
1335 }
1336 }
1337 }
1338 frameStack = frameStack.prepend(new Frame(tree));
1339 super.visitClassDef(tree);
1340 }
1341 finally {
1342 log.useSource(prevSource.getFile());
1343 frameStack = prevStack;
1344 lambdaCount = prevLambdaCount;
1345 syntheticMethodNameCounts = prevSyntheticMethodNameCounts;
1346 clinits = prevClinits;
1347 }
1348 }
1349
1350 @Override
1351 public void visitIdent(JCIdent tree) {
1352 if (context() != null && lambdaIdentSymbolFilter(tree.sym)) {
1353 if (tree.sym.kind == VAR &&
1354 tree.sym.owner.kind == MTH &&
1355 tree.type.constValue() == null) {
1356 TranslationContext<?> localContext = context();
1357 while (localContext != null) {
1358 if (localContext.tree.getTag() == LAMBDA) {
1359 JCTree block = capturedDecl(localContext.depth, tree.sym);
1360 if (block == null) break;
1361 ((LambdaTranslationContext)localContext)
1362 .addSymbol(tree.sym, CAPTURED_VAR);
1363 }
1364 localContext = localContext.prev;
1365 }
1366 } else if (tree.sym.owner.kind == TYP) {
1367 TranslationContext<?> localContext = context();
1368 while (localContext != null && !localContext.owner.isStatic()) {
1369 if (localContext.tree.hasTag(LAMBDA)) {
1370 JCTree block = capturedDecl(localContext.depth, tree.sym);
1371 if (block == null) break;
1372 switch (block.getTag()) {
1373 case CLASSDEF:
1374 JCClassDecl cdecl = (JCClassDecl)block;
1375 ((LambdaTranslationContext)localContext)
1376 .addSymbol(cdecl.sym, CAPTURED_THIS);
1377 break;
1378 default:
1379 Assert.error("bad block kind");
1380 }
1381 }
1382 localContext = localContext.prev;
1383 }
1384 }
1385 }
1386 super.visitIdent(tree);
1387 }
1388
1389 @Override
1390 public void visitLambda(JCLambda tree) {
1391 analyzeLambda(tree, "lambda.stat");
1392 }
1393
1394 private void analyzeLambda(JCLambda tree, JCExpression methodReferenceReceiver) {
1395 // Translation of the receiver expression must occur first
1396 JCExpression rcvr = translate(methodReferenceReceiver);
1397 LambdaTranslationContext context = analyzeLambda(tree, "mref.stat.1");
1398 if (rcvr != null) {
1399 context.methodReferenceReceiver = rcvr;
1400 }
1401 }
1402
1403 private LambdaTranslationContext analyzeLambda(JCLambda tree, String statKey) {
1404 List<Frame> prevStack = frameStack;
1405 try {
1406 LambdaTranslationContext context = new LambdaTranslationContext(tree);
1407 frameStack = frameStack.prepend(new Frame(tree));
1408 for (JCVariableDecl param : tree.params) {
1409 context.addSymbol(param.sym, PARAM);
1410 frameStack.head.addLocal(param.sym);
1411 }
1412 contextMap.put(tree, context);
1413 super.visitLambda(tree);
1414 context.complete();
1415 if (dumpLambdaToMethodStats) {
1416 log.note(tree, diags.noteKey(statKey, context.needsAltMetafactory(), context.translatedSym));
1417 }
1418 return context;
1419 }
1420 finally {
1421 frameStack = prevStack;
1422 }
1423 }
1424
1425 @Override
1426 public void visitMethodDef(JCMethodDecl tree) {
1427 List<Frame> prevStack = frameStack;
1428 try {
1429 frameStack = frameStack.prepend(new Frame(tree));
1430 super.visitMethodDef(tree);
1431 }
1432 finally {
1433 frameStack = prevStack;
1434 }
1435 }
1436
1437 @Override
1438 public void visitNewClass(JCNewClass tree) {
1439 TypeSymbol def = tree.type.tsym;
1440 boolean inReferencedClass = currentlyInClass(def);
1441 boolean isLocal = def.isLocal();
1442 if ((inReferencedClass && isLocal || lambdaNewClassFilter(context(), tree))) {
1443 TranslationContext<?> localContext = context();
1444 final TypeSymbol outerInstanceSymbol = tree.type.getEnclosingType().tsym;
1445 while (localContext != null && !localContext.owner.isStatic()) {
1446 if (localContext.tree.hasTag(LAMBDA)) {
1447 if (outerInstanceSymbol != null) {
1448 JCTree block = capturedDecl(localContext.depth, outerInstanceSymbol);
1449 if (block == null) break;
1450 }
1451 ((LambdaTranslationContext)localContext)
1452 .addSymbol(outerInstanceSymbol, CAPTURED_THIS);
1453 }
1454 localContext = localContext.prev;
1455 }
1456 }
1457 if (context() != null && !inReferencedClass && isLocal) {
1458 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context();
1459 captureLocalClassDefs(def, lambdaContext);
1460 }
1461 super.visitNewClass(tree);
1462 }
1463 //where
1464 void captureLocalClassDefs(Symbol csym, final LambdaTranslationContext lambdaContext) {
1465 JCClassDecl localCDef = localClassDefs.get(csym);
1466 if (localCDef != null && lambdaContext.freeVarProcessedLocalClasses.add(csym)) {
1467 BasicFreeVarCollector fvc = lower.new BasicFreeVarCollector() {
1468 @Override
1469 void addFreeVars(ClassSymbol c) {
1470 captureLocalClassDefs(c, lambdaContext);
1471 }
1472 @Override
1473 void visitSymbol(Symbol sym) {
1474 if (sym.kind == VAR &&
1475 sym.owner.kind == MTH &&
1476 ((VarSymbol)sym).getConstValue() == null) {
1477 TranslationContext<?> localContext = context();
1478 while (localContext != null) {
1479 if (localContext.tree.getTag() == LAMBDA) {
1480 JCTree block = capturedDecl(localContext.depth, sym);
1481 if (block == null) break;
1482 ((LambdaTranslationContext)localContext).addSymbol(sym, CAPTURED_VAR);
1483 }
1484 localContext = localContext.prev;
1485 }
1486 }
1487 }
1488 };
1489 fvc.scan(localCDef);
1490 }
1491 }
1492 //where
1493 boolean currentlyInClass(Symbol csym) {
1494 for (Frame frame : frameStack) {
1495 if (frame.tree.hasTag(JCTree.Tag.CLASSDEF)) {
1496 JCClassDecl cdef = (JCClassDecl) frame.tree;
1497 if (cdef.sym == csym) {
1498 return true;
1499 }
1500 }
1501 }
1502 return false;
1503 }
1504
1505 /**
1506 * Method references to local class constructors, may, if the local
1507 * class references local variables, have implicit constructor
1508 * parameters added in Lower; As a result, the invokedynamic bootstrap
1509 * information added in the LambdaToMethod pass will have the wrong
1510 * signature. Hooks between Lower and LambdaToMethod have been added to
1511 * handle normal "new" in this case. This visitor converts potentially
1512 * affected method references into a lambda containing a normal
1513 * expression.
1514 *
1515 * @param tree
1516 */
1517 @Override
1518 public void visitReference(JCMemberReference tree) {
1519 ReferenceTranslationContext rcontext = new ReferenceTranslationContext(tree);
1520 contextMap.put(tree, rcontext);
1521 if (rcontext.needsConversionToLambda()) {
1522 // Convert to a lambda, and process as such
1523 MemberReferenceToLambda conv = new MemberReferenceToLambda(tree, rcontext, owner());
1524 analyzeLambda(conv.lambda(), conv.getReceiverExpression());
1525 } else {
1526 super.visitReference(tree);
1527 if (dumpLambdaToMethodStats) {
1528 log.note(tree, Notes.MrefStat(rcontext.needsAltMetafactory(), null));
1529 }
1530 }
1531 }
1532
1533 @Override
1534 public void visitSelect(JCFieldAccess tree) {
1535 if (context() != null && tree.sym.kind == VAR &&
1536 (tree.sym.name == names._this ||
1537 tree.sym.name == names._super)) {
1538 // A select of this or super means, if we are in a lambda,
1539 // we much have an instance context
1540 TranslationContext<?> localContext = context();
1541 while (localContext != null && !localContext.owner.isStatic()) {
1542 if (localContext.tree.hasTag(LAMBDA)) {
1543 JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym);
1544 if (clazz == null) break;
1545 ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS);
1546 }
1547 localContext = localContext.prev;
1548 }
1549 }
1550 super.visitSelect(tree);
1551 }
1552
1553 @Override
1554 public void visitVarDef(JCVariableDecl tree) {
1555 TranslationContext<?> context = context();
1556 LambdaTranslationContext ltc = (context != null && context instanceof LambdaTranslationContext)?
1557 (LambdaTranslationContext)context :
1558 null;
1559 if (ltc != null) {
1560 if (frameStack.head.tree.hasTag(LAMBDA)) {
1561 ltc.addSymbol(tree.sym, LOCAL_VAR);
1562 }
1563 // Check for type variables (including as type arguments).
1564 // If they occur within class nested in a lambda, mark for erasure
1565 Type type = tree.sym.asType();
1566 }
1567
1568 List<Frame> prevStack = frameStack;
1569 try {
1570 if (tree.sym.owner.kind == MTH) {
1571 frameStack.head.addLocal(tree.sym);
1572 }
1573 frameStack = frameStack.prepend(new Frame(tree));
1574 super.visitVarDef(tree);
1575 }
1576 finally {
1577 frameStack = prevStack;
1578 }
1579 }
1580
1581 /**
1582 * Return a valid owner given the current declaration stack
1583 * (required to skip synthetic lambda symbols)
1584 */
1585 private Symbol owner() {
1586 return owner(false);
1587 }
1588
1589 @SuppressWarnings("fallthrough")
1590 private Symbol owner(boolean skipLambda) {
1591 List<Frame> frameStack2 = frameStack;
1592 while (frameStack2.nonEmpty()) {
1593 switch (frameStack2.head.tree.getTag()) {
1594 case VARDEF:
1595 if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) {
1596 frameStack2 = frameStack2.tail;
1597 break;
1598 }
1599 JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree;
1600 return initSym(cdecl.sym,
1601 ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC);
1602 case BLOCK:
1603 JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree;
1604 return initSym(cdecl2.sym,
1605 ((JCBlock)frameStack2.head.tree).flags & STATIC);
1606 case CLASSDEF:
1607 return ((JCClassDecl)frameStack2.head.tree).sym;
1608 case METHODDEF:
1609 return ((JCMethodDecl)frameStack2.head.tree).sym;
1610 case LAMBDA:
1611 if (!skipLambda)
1612 return ((LambdaTranslationContext)contextMap
1613 .get(frameStack2.head.tree)).translatedSym;
1614 default:
1615 frameStack2 = frameStack2.tail;
1616 }
1617 }
1618 Assert.error();
1619 return null;
1620 }
1621
1622 private Symbol initSym(ClassSymbol csym, long flags) {
1623 boolean isStatic = (flags & STATIC) != 0;
1624 if (isStatic) {
1625 /* static clinits are generated in Gen, so we need to use a fake
1626 * one. Attr creates a fake clinit method while attributing
1627 * lambda expressions used as initializers of static fields, so
1628 * let's use that one.
1629 */
1630 MethodSymbol clinit = attr.removeClinit(csym);
1631 if (clinit != null) {
1632 clinits.put(csym, clinit);
1633 return clinit;
1634 }
1635
1636 /* if no clinit is found at Attr, then let's try at clinits.
1637 */
1638 clinit = (MethodSymbol)clinits.get(csym);
1639 if (clinit == null) {
1640 /* no luck, let's create a new one
1641 */
1642 clinit = makePrivateSyntheticMethod(STATIC,
1643 names.clinit,
1644 new MethodType(List.nil(), syms.voidType,
1645 List.nil(), syms.methodClass),
1646 csym);
1647 clinits.put(csym, clinit);
1648 }
1649 return clinit;
1650 } else {
1651 //get the first constructor and treat it as the instance init sym
1652 for (Symbol s : csym.members_field.getSymbolsByName(names.init)) {
1653 return s;
1654 }
1655 }
1656 Assert.error("init not found");
1657 return null;
1658 }
1659
1660 private JCTree directlyEnclosingLambda() {
1661 if (frameStack.isEmpty()) {
1662 return null;
1663 }
1664 List<Frame> frameStack2 = frameStack;
1665 while (frameStack2.nonEmpty()) {
1666 switch (frameStack2.head.tree.getTag()) {
1667 case CLASSDEF:
1668 case METHODDEF:
1669 return null;
1670 case LAMBDA:
1671 return frameStack2.head.tree;
1672 default:
1673 frameStack2 = frameStack2.tail;
1674 }
1675 }
1676 Assert.error();
1677 return null;
1678 }
1679
1680 private boolean inClassWithinLambda() {
1681 if (frameStack.isEmpty()) {
1682 return false;
1683 }
1684 List<Frame> frameStack2 = frameStack;
1685 boolean classFound = false;
1686 while (frameStack2.nonEmpty()) {
1687 switch (frameStack2.head.tree.getTag()) {
1688 case LAMBDA:
1689 return classFound;
1690 case CLASSDEF:
1691 classFound = true;
1692 frameStack2 = frameStack2.tail;
1693 break;
1694 default:
1695 frameStack2 = frameStack2.tail;
1696 }
1697 }
1698 // No lambda
1699 return false;
1700 }
1701
1702 /**
1703 * Return the declaration corresponding to a symbol in the enclosing
1704 * scope; the depth parameter is used to filter out symbols defined
1705 * in nested scopes (which do not need to undergo capture).
1706 */
1707 private JCTree capturedDecl(int depth, Symbol sym) {
1708 int currentDepth = frameStack.size() - 1;
1709 for (Frame block : frameStack) {
1710 switch (block.tree.getTag()) {
1711 case CLASSDEF:
1712 ClassSymbol clazz = ((JCClassDecl)block.tree).sym;
1713 if (clazz.isSubClass(sym, types) || sym.isMemberOf(clazz, types)) {
1714 return currentDepth > depth ? null : block.tree;
1715 }
1716 break;
1717 case VARDEF:
1718 if ((((JCVariableDecl)block.tree).sym == sym &&
1719 sym.owner.kind == MTH) || //only locals are captured
1720 (block.locals != null && block.locals.contains(sym))) {
1721 return currentDepth > depth ? null : block.tree;
1722 }
1723 break;
1724 case BLOCK:
1725 case METHODDEF:
1726 case LAMBDA:
1727 if (block.locals != null && block.locals.contains(sym)) {
1728 return currentDepth > depth ? null : block.tree;
1729 }
1730 break;
1731 default:
1732 Assert.error("bad decl kind " + block.tree.getTag());
1733 }
1734 currentDepth--;
1735 }
1736 return null;
1737 }
1738
1739 private TranslationContext<?> context() {
1740 for (Frame frame : frameStack) {
1741 TranslationContext<?> context = contextMap.get(frame.tree);
1742 if (context != null) {
1743 return context;
1744 }
1745 }
1746 return null;
1747 }
1748
1749 /**
1750 * This is used to filter out those identifiers that needs to be adjusted
1751 * when translating away lambda expressions
1752 */
1753 private boolean lambdaIdentSymbolFilter(Symbol sym) {
1754 return (sym.kind == VAR || sym.kind == MTH)
1755 && !sym.isStatic()
1756 && sym.name != names.init;
1757 }
1758
1759 /**
1760 * This is used to filter out those select nodes that need to be adjusted
1761 * when translating away lambda expressions - at the moment, this is the
1762 * set of nodes that select `this' (qualified this)
1763 */
1764 private boolean lambdaFieldAccessFilter(JCFieldAccess fAccess) {
1765 LambdaTranslationContext lambdaContext =
1766 context instanceof LambdaTranslationContext ?
1767 (LambdaTranslationContext) context : null;
1768 return lambdaContext != null
1769 && !fAccess.sym.isStatic()
1770 && fAccess.name == names._this
1771 && (fAccess.sym.owner.kind == TYP)
1772 && !lambdaContext.translatedSymbols.get(CAPTURED_OUTER_THIS).isEmpty();
1773 }
1774
1775 /**
1776 * This is used to filter out those new class expressions that need to
1777 * be qualified with an enclosing tree
1778 */
1779 private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) {
1780 if (context != null
1781 && tree.encl == null
1782 && tree.def == null
1783 && !tree.type.getEnclosingType().hasTag(NONE)) {
1784 Type encl = tree.type.getEnclosingType();
1785 Type current = context.owner.enclClass().type;
1786 while (!current.hasTag(NONE)) {
1787 if (current.tsym.isSubClass(encl.tsym, types)) {
1788 return true;
1789 }
1790 current = current.getEnclosingType();
1791 }
1792 return false;
1793 } else {
1794 return false;
1795 }
1796 }
1797
1798 private class Frame {
1799 final JCTree tree;
1800 List<Symbol> locals;
1801
1802 public Frame(JCTree tree) {
1803 this.tree = tree;
1804 }
1805
1806 void addLocal(Symbol sym) {
1807 if (locals == null) {
1808 locals = List.nil();
1809 }
1810 locals = locals.prepend(sym);
1811 }
1812 }
1813
1814 /**
1815 * This class is used to store important information regarding translation of
1816 * lambda expression/method references (see subclasses).
1817 */
1818 abstract class TranslationContext<T extends JCFunctionalExpression> {
1819
1820 /** the underlying (untranslated) tree */
1821 final T tree;
1822
1823 /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */
1824 final Symbol owner;
1825
1826 /** the depth of this lambda expression in the frame stack */
1827 final int depth;
1828
1829 /** the enclosing translation context (set for nested lambdas/mref) */
1830 final TranslationContext<?> prev;
1831
1832 /** list of methods to be bridged by the meta-factory */
1833 final List<Symbol> bridges;
1834
1835 TranslationContext(T tree) {
1836 this.tree = tree;
1837 this.owner = owner(true);
1838 this.depth = frameStack.size() - 1;
1839 this.prev = context();
1840 ClassSymbol csym =
1841 types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.target, ABSTRACT | INTERFACE);
1842 this.bridges = types.functionalInterfaceBridges(csym);
1843 }
1844
1845 /** does this functional expression need to be created using alternate metafactory? */
1846 boolean needsAltMetafactory() {
1847 return tree.target.isIntersection() ||
1848 isSerializable() ||
1849 bridges.length() > 1;
1850 }
1851
1852 /** does this functional expression require serialization support? */
1853 boolean isSerializable() {
1854 if (forceSerializable) {
1855 return true;
1856 }
1857 return types.asSuper(tree.target, syms.serializableType.tsym) != null;
1858 }
1859
1860 /**
1861 * @return Name of the enclosing method to be folded into synthetic
1862 * method name
1863 */
1864 String enclosingMethodName() {
1865 return syntheticMethodNameComponent(owner.name);
1866 }
1867
1868 /**
1869 * @return Method name in a form that can be folded into a
1870 * component of a synthetic method name
1871 */
1872 String syntheticMethodNameComponent(Name name) {
1873 if (name == null) {
1874 return "null";
1875 }
1876 String methodName = name.toString();
1877 if (methodName.equals("<clinit>")) {
1878 methodName = "static";
1879 } else if (methodName.equals("<init>")) {
1880 methodName = "new";
1881 }
1882 return methodName;
1883 }
1884 }
1885
1886 /**
1887 * This class retains all the useful information about a lambda expression;
1888 * the contents of this class are filled by the LambdaAnalyzer visitor,
1889 * and the used by the main translation routines in order to adjust references
1890 * to captured locals/members, etc.
1891 */
1892 class LambdaTranslationContext extends TranslationContext<JCLambda> {
1893
1894 /** variable in the enclosing context to which this lambda is assigned */
1895 final Symbol self;
1896
1897 /** variable in the enclosing context to which this lambda is assigned */
1898 final Symbol assignedTo;
1899
1900 Map<LambdaSymbolKind, Map<Symbol, Symbol>> translatedSymbols;
1901
1902 /** the synthetic symbol for the method hoisting the translated lambda */
1903 MethodSymbol translatedSym;
1904
1905 List<JCVariableDecl> syntheticParams;
1906
1907 /**
1908 * to prevent recursion, track local classes processed
1909 */
1910 final Set<Symbol> freeVarProcessedLocalClasses;
1911
1912 /**
1913 * For method references converted to lambdas. The method
1914 * reference receiver expression. Must be treated like a captured
1915 * variable.
1916 */
1917 JCExpression methodReferenceReceiver;
1918
1919 LambdaTranslationContext(JCLambda tree) {
1920 super(tree);
1921 Frame frame = frameStack.head;
1922 switch (frame.tree.getTag()) {
1923 case VARDEF:
1924 assignedTo = self = ((JCVariableDecl) frame.tree).sym;
1925 break;
1926 case ASSIGN:
1927 self = null;
1928 assignedTo = TreeInfo.symbol(((JCAssign) frame.tree).getVariable());
1929 break;
1930 default:
1931 assignedTo = self = null;
1932 break;
1933 }
1934
1935 // This symbol will be filled-in in complete
1936 if (owner.kind == MTH) {
1937 final MethodSymbol originalOwner = (MethodSymbol)owner.clone(owner.owner);
1938 this.translatedSym = new MethodSymbol(SYNTHETIC | PRIVATE, null, null, owner.enclClass()) {
1939 @Override
1940 public MethodSymbol originalEnclosingMethod() {
1941 return originalOwner;
1942 }
1943 };
1944 } else {
1945 this.translatedSym = makePrivateSyntheticMethod(0, null, null, owner.enclClass());
1946 }
1947 translatedSymbols = new EnumMap<>(LambdaSymbolKind.class);
1948
1949 translatedSymbols.put(PARAM, new LinkedHashMap<Symbol, Symbol>());
1950 translatedSymbols.put(LOCAL_VAR, new LinkedHashMap<Symbol, Symbol>());
1951 translatedSymbols.put(CAPTURED_VAR, new LinkedHashMap<Symbol, Symbol>());
1952 translatedSymbols.put(CAPTURED_THIS, new LinkedHashMap<Symbol, Symbol>());
1953 translatedSymbols.put(CAPTURED_OUTER_THIS, new LinkedHashMap<Symbol, Symbol>());
1954
1955 freeVarProcessedLocalClasses = new HashSet<>();
1956 }
1957
1958 /**
1959 * For a serializable lambda, generate a disambiguating string
1960 * which maximizes stability across deserialization.
1961 *
1962 * @return String to differentiate synthetic lambda method names
1963 */
1964 private String serializedLambdaDisambiguation() {
1965 StringBuilder buf = new StringBuilder();
1966 // Append the enclosing method signature to differentiate
1967 // overloaded enclosing methods. For lambdas enclosed in
1968 // lambdas, the generated lambda method will not have type yet,
1969 // but the enclosing method's name will have been generated
1970 // with this same method, so it will be unique and never be
1971 // overloaded.
1972 Assert.check(
1973 owner.type != null ||
1974 directlyEnclosingLambda() != null);
1975 if (owner.type != null) {
1976 buf.append(typeSig(owner.type, true));
1977 buf.append(":");
1978 }
1979
1980 // Add target type info
1981 buf.append(types.findDescriptorSymbol(tree.type.tsym).owner.flatName());
1982 buf.append(" ");
1983
1984 // Add variable assigned to
1985 if (assignedTo != null) {
1986 buf.append(assignedTo.flatName());
1987 buf.append("=");
1988 }
1989 //add captured locals info: type, name, order
1990 for (Symbol fv : getSymbolMap(CAPTURED_VAR).keySet()) {
1991 if (fv != self) {
1992 buf.append(typeSig(fv.type, true));
1993 buf.append(" ");
1994 buf.append(fv.flatName());
1995 buf.append(",");
1996 }
1997 }
1998
1999 return buf.toString();
2000 }
2001
2002 /**
2003 * For a non-serializable lambda, generate a simple method.
2004 *
2005 * @return Name to use for the synthetic lambda method name
2006 */
2007 private Name lambdaName() {
2008 return names.lambda.append(names.fromString(enclosingMethodName() + "$" + lambdaCount++));
2009 }
2010
2011 /**
2012 * For a serializable lambda, generate a method name which maximizes
2013 * name stability across deserialization.
2014 *
2015 * @return Name to use for the synthetic lambda method name
2016 */
2017 private Name serializedLambdaName() {
2018 StringBuilder buf = new StringBuilder();
2019 buf.append(names.lambda);
2020 // Append the name of the method enclosing the lambda.
2021 buf.append(enclosingMethodName());
2022 buf.append('$');
2023 // Append a hash of the disambiguating string : enclosing method
2024 // signature, etc.
2025 String disam = serializedLambdaDisambiguation();
2026 buf.append(Integer.toHexString(disam.hashCode()));
2027 buf.append('$');
2028 // The above appended name components may not be unique, append
2029 // a count based on the above name components.
2030 buf.append(syntheticMethodNameCounts.getIndex(buf));
2031 String result = buf.toString();
2032 //System.err.printf("serializedLambdaName: %s -- %s\n", result, disam);
2033 return names.fromString(result);
2034 }
2035
2036 /**
2037 * Translate a symbol of a given kind into something suitable for the
2038 * synthetic lambda body
2039 */
2040 Symbol translate(final Symbol sym, LambdaSymbolKind skind) {
2041 Symbol ret;
2042 switch (skind) {
2043 case CAPTURED_THIS:
2044 ret = sym; // self represented
2045 break;
2046 case CAPTURED_VAR:
2047 ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, sym.name, types.erasure(sym.type), translatedSym) {
2048 @Override
2049 public Symbol baseSymbol() {
2050 //keep mapping with original captured symbol
2051 return sym;
2052 }
2053 };
2054 break;
2055 case CAPTURED_OUTER_THIS:
2056 Name name = names.fromString(new String(sym.flatName().toString().replace('.', '$') + names.dollarThis));
2057 ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym) {
2058 @Override
2059 public Symbol baseSymbol() {
2060 //keep mapping with original captured symbol
2061 return sym;
2062 }
2063 };
2064 break;
2065 case LOCAL_VAR:
2066 ret = new VarSymbol(sym.flags() & FINAL, sym.name, sym.type, translatedSym);
2067 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
2068 break;
2069 case PARAM:
2070 ret = new VarSymbol((sym.flags() & FINAL) | PARAMETER, sym.name, types.erasure(sym.type), translatedSym);
2071 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
2072 break;
2073 default:
2074 Assert.error(skind.name());
2075 throw new AssertionError();
2076 }
2077 if (ret != sym && skind.propagateAnnotations()) {
2078 ret.setDeclarationAttributes(sym.getRawAttributes());
2079 ret.setTypeAttributes(sym.getRawTypeAttributes());
2080 }
2081 return ret;
2082 }
2083
2084 void addSymbol(Symbol sym, LambdaSymbolKind skind) {
2085 if (skind == CAPTURED_THIS && sym != null && sym.kind == TYP && !typesUnderConstruction.isEmpty()) {
2086 ClassSymbol currentClass = currentClass();
2087 if (currentClass != null && typesUnderConstruction.contains(currentClass)) {
2088 // reference must be to enclosing outer instance, mutate capture kind.
2089 Assert.check(sym != currentClass); // should have been caught right in Attr
2090 skind = CAPTURED_OUTER_THIS;
2091 }
2092 }
2093 Map<Symbol, Symbol> transMap = getSymbolMap(skind);
2094 if (!transMap.containsKey(sym)) {
2095 transMap.put(sym, translate(sym, skind));
2096 }
2097 }
2098
2099 Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind skind) {
2100 Map<Symbol, Symbol> m = translatedSymbols.get(skind);
2101 Assert.checkNonNull(m);
2102 return m;
2103 }
2104
2105 JCTree translate(JCIdent lambdaIdent) {
2106 for (LambdaSymbolKind kind : LambdaSymbolKind.values()) {
2107 Map<Symbol, Symbol> m = getSymbolMap(kind);
2108 switch(kind) {
2109 default:
2110 if (m.containsKey(lambdaIdent.sym)) {
2111 Symbol tSym = m.get(lambdaIdent.sym);
2112 JCTree t = make.Ident(tSym).setType(lambdaIdent.type);
2113 return t;
2114 }
2115 break;
2116 case CAPTURED_OUTER_THIS:
2117 Optional<Symbol> proxy = m.keySet().stream()
2118 .filter(out -> lambdaIdent.sym.isMemberOf(out.type.tsym, types))
2119 .reduce((a, b) -> a.isEnclosedBy((ClassSymbol)b) ? a : b);
2120 if (proxy.isPresent()) {
2121 // Transform outer instance variable references anchoring them to the captured synthetic.
2122 Symbol tSym = m.get(proxy.get());
2123 JCExpression t = make.Ident(tSym).setType(lambdaIdent.sym.owner.type);
2124 t = make.Select(t, lambdaIdent.name);
2125 t.setType(lambdaIdent.type);
2126 TreeInfo.setSymbol(t, lambdaIdent.sym);
2127 return t;
2128 }
2129 break;
2130 }
2131 }
2132 return null;
2133 }
2134
2135 /* Translate away qualified this expressions, anchoring them to synthetic parameters that
2136 capture the qualified this handle. `fieldAccess' is guaranteed to one such.
2137 */
2138 public JCTree translate(JCFieldAccess fieldAccess) {
2139 Assert.check(fieldAccess.name == names._this);
2140 Map<Symbol, Symbol> m = translatedSymbols.get(LambdaSymbolKind.CAPTURED_OUTER_THIS);
2141 if (m.containsKey(fieldAccess.sym.owner)) {
2142 Symbol tSym = m.get(fieldAccess.sym.owner);
2143 JCExpression t = make.Ident(tSym).setType(fieldAccess.sym.owner.type);
2144 return t;
2145 }
2146 return null;
2147 }
2148
2149 /* Translate away naked new instance creation expressions with implicit enclosing instances,
2150 anchoring them to synthetic parameters that stand proxy for the qualified outer this handle.
2151 */
2152 public JCNewClass translate(JCNewClass newClass) {
2153 Assert.check(newClass.clazz.type.tsym.hasOuterInstance() && newClass.encl == null);
2154 Map<Symbol, Symbol> m = translatedSymbols.get(LambdaSymbolKind.CAPTURED_OUTER_THIS);
2155 final Type enclosingType = newClass.clazz.type.getEnclosingType();
2156 if (m.containsKey(enclosingType.tsym)) {
2157 Symbol tSym = m.get(enclosingType.tsym);
2158 JCExpression encl = make.Ident(tSym).setType(enclosingType);
2159 newClass.encl = encl;
2160 }
2161 return newClass;
2162 }
2163
2164 /**
2165 * The translatedSym is not complete/accurate until the analysis is
2166 * finished. Once the analysis is finished, the translatedSym is
2167 * "completed" -- updated with type information, access modifiers,
2168 * and full parameter list.
2169 */
2170 void complete() {
2171 if (syntheticParams != null) {
2172 return;
2173 }
2174 boolean inInterface = translatedSym.owner.isInterface();
2175 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty();
2176
2177 // If instance access isn't needed, make it static.
2178 // Interface instance methods must be default methods.
2179 // Lambda methods are private synthetic.
2180 // Inherit ACC_STRICT from the enclosing method, or, for clinit,
2181 // from the class.
2182 translatedSym.flags_field = SYNTHETIC | LAMBDA_METHOD |
2183 owner.flags_field & STRICTFP |
2184 owner.owner.flags_field & STRICTFP |
2185 PRIVATE |
2186 (thisReferenced? (inInterface? DEFAULT : 0) : STATIC);
2187
2188 //compute synthetic params
2189 ListBuffer<JCVariableDecl> params = new ListBuffer<>();
2190 ListBuffer<VarSymbol> parameterSymbols = new ListBuffer<>();
2191
2192 // The signature of the method is augmented with the following
2193 // synthetic parameters:
2194 //
2195 // 1) reference to enclosing contexts captured by the lambda expression
2196 // 2) enclosing locals captured by the lambda expression
2197 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR).values()) {
2198 params.append(make.VarDef((VarSymbol) thisSym, null));
2199 parameterSymbols.append((VarSymbol) thisSym);
2200 }
2201 for (Symbol thisSym : getSymbolMap(CAPTURED_OUTER_THIS).values()) {
2202 params.append(make.VarDef((VarSymbol) thisSym, null));
2203 parameterSymbols.append((VarSymbol) thisSym);
2204 }
2205 for (Symbol thisSym : getSymbolMap(PARAM).values()) {
2206 params.append(make.VarDef((VarSymbol) thisSym, null));
2207 parameterSymbols.append((VarSymbol) thisSym);
2208 }
2209 syntheticParams = params.toList();
2210
2211 translatedSym.params = parameterSymbols.toList();
2212
2213 // Compute and set the lambda name
2214 translatedSym.name = isSerializable()
2215 ? serializedLambdaName()
2216 : lambdaName();
2217
2218 //prepend synthetic args to translated lambda method signature
2219 translatedSym.type = types.createMethodTypeWithParameters(
2220 generatedLambdaSig(),
2221 TreeInfo.types(syntheticParams));
2222 }
2223
2224 Type generatedLambdaSig() {
2225 return types.erasure(tree.getDescriptorType(types));
2226 }
2227 }
2228
2229 /**
2230 * This class retains all the useful information about a method reference;
2231 * the contents of this class are filled by the LambdaAnalyzer visitor,
2232 * and the used by the main translation routines in order to adjust method
2233 * references (i.e. in case a bridge is needed)
2234 */
2235 final class ReferenceTranslationContext extends TranslationContext<JCMemberReference> {
2236
2237 final boolean isSuper;
2238
2239 ReferenceTranslationContext(JCMemberReference tree) {
2240 super(tree);
2241 this.isSuper = tree.hasKind(ReferenceKind.SUPER);
2242 }
2243
2244 boolean needsVarArgsConversion() {
2245 return tree.varargsElement != null;
2246 }
2247
2248 /**
2249 * @return Is this an array operation like clone()
2250 */
2251 boolean isArrayOp() {
2252 return tree.sym.owner == syms.arrayClass;
2253 }
2254
2255 boolean receiverAccessible() {
2256 //hack needed to workaround 292 bug (7087658)
2257 //when 292 issue is fixed we should remove this and change the backend
2258 //code to always generate a method handle to an accessible method
2259 return tree.ownerAccessible;
2260 }
2261
2262 /* Per our interim inline class translation scheme, the reference projection classes
2263 are completely empty, so we want the methods in the value class to be invoked instead.
2264 As the lambda meta factory isn't clued into this, it will try to invoke the method in
2265 C$ref.class and fail with a NoSuchMethodError. we need to workaround it ourselves.
2266 */
2267 boolean receiverIsReferenceProjection() {
2268 return tree.sym.kind == MTH && tree.sym.owner.isReferenceProjection();
2269 }
2270
2271 /**
2272 * This method should be called only when target release <= 14
2273 * where LambdaMetaFactory does not spin nestmate classes.
2274 *
2275 * This method should be removed when --release 14 is not supported.
2276 */
2277 boolean isPrivateInOtherClass() {
2278 assert !nestmateLambdas;
2279 return (tree.sym.flags() & PRIVATE) != 0 &&
2280 !types.isSameType(
2281 types.erasure(tree.sym.enclClass().asType()),
2282 types.erasure(owner.enclClass().asType()));
2283 }
2284
2285 /**
2286 * Erasure destroys the implementation parameter subtype
2287 * relationship for intersection types.
2288 * Have similar problems for union types too.
2289 */
2290 boolean interfaceParameterIsIntersectionOrUnionType() {
2291 List<Type> tl = tree.getDescriptorType(types).getParameterTypes();
2292 for (; tl.nonEmpty(); tl = tl.tail) {
2293 Type pt = tl.head;
2294 return isIntersectionOrUnionType(pt);
2295 }
2296 return false;
2297 }
2298
2299 boolean isIntersectionOrUnionType(Type t) {
2300 switch (t.getKind()) {
2301 case INTERSECTION:
2302 case UNION:
2303 return true;
2304 case TYPEVAR:
2305 TypeVar tv = (TypeVar) t;
2306 return isIntersectionOrUnionType(tv.getUpperBound());
2307 }
2308 return false;
2309 }
2310
2311 /**
2312 * Does this reference need to be converted to a lambda
2313 * (i.e. var args need to be expanded or "super" is used)
2314 */
2315 final boolean needsConversionToLambda() {
2316 return interfaceParameterIsIntersectionOrUnionType() ||
2317 isSuper ||
2318 needsVarArgsConversion() ||
2319 isArrayOp() ||
2320 (!nestmateLambdas && isPrivateInOtherClass()) ||
2321 isProtectedInSuperClassOfEnclosingClassInOtherPackage(tree.sym, owner) ||
2322 !receiverAccessible() ||
2323 receiverIsReferenceProjection() ||
2324 (tree.getMode() == ReferenceMode.NEW &&
2325 tree.kind != ReferenceKind.ARRAY_CTOR &&
2326 (tree.sym.owner.isLocal() || tree.sym.owner.isInner() || tree.sym.owner.isValue()));
2327 }
2328
2329 Type generatedRefSig() {
2330 return types.erasure(tree.sym.type);
2331 }
2332
2333 Type bridgedRefSig() {
2334 return types.erasure(types.findDescriptorSymbol(tree.target.tsym).type);
2335 }
2336 }
2337 }
2338 // </editor-fold>
2339
2340 /*
2341 * These keys provide mappings for various translated lambda symbols
2342 * and the prevailing order must be maintained.
2343 */
2344 enum LambdaSymbolKind {
2345 PARAM, // original to translated lambda parameters
2346 LOCAL_VAR, // original to translated lambda locals
2347 CAPTURED_VAR, // variables in enclosing scope to translated synthetic parameters
2348 CAPTURED_THIS, // class symbols to translated synthetic parameters (for captured member access)
2349 CAPTURED_OUTER_THIS; // used when `this' capture is illegal, but outer this capture is legit (JDK-8129740)
2350
2351 boolean propagateAnnotations() {
2352 switch (this) {
2353 case CAPTURED_VAR:
2354 case CAPTURED_THIS:
2355 case CAPTURED_OUTER_THIS:
2356 return false;
2357 default:
2358 return true;
2359 }
2360 }
2361 }
2362
2363 /**
2364 * ****************************************************************
2365 * Signature Generation
2366 * ****************************************************************
2367 */
2368
2369 private String typeSig(Type type) {
2370 return typeSig(type, false);
2371 }
2372
2373 private String typeSig(Type type, boolean allowIllegalSignature) {
2374 try {
2375 L2MSignatureGenerator sg = new L2MSignatureGenerator(allowIllegalSignature);
2376 sg.assembleSig(type);
2377 return sg.toString();
2378 } catch (InvalidSignatureException ex) {
2379 Symbol c = attrEnv.enclClass.sym;
2380 log.error(Errors.CannotGenerateClass(c, Fragments.IllegalSignature(c, ex.type())));
2381 return "<ERRONEOUS>";
2382 }
2383 }
2384
2385 private String classSig(Type type) {
2386 try {
2387 L2MSignatureGenerator sg = new L2MSignatureGenerator(false);
2388 sg.assembleClassSig(type);
2389 return sg.toString();
2390 } catch (InvalidSignatureException ex) {
2391 Symbol c = attrEnv.enclClass.sym;
2392 log.error(Errors.CannotGenerateClass(c, Fragments.IllegalSignature(c, ex.type())));
2393 return "<ERRONEOUS>";
2394 }
2395 }
2396
2397 private boolean isProtectedInSuperClassOfEnclosingClassInOtherPackage(Symbol targetReference,
2398 Symbol currentClass) {
2399 return ((targetReference.flags() & PROTECTED) != 0 &&
2400 targetReference.packge() != currentClass.packge());
2401 }
2402
2403 /**
2404 * Signature Generation
2405 */
2406 private class L2MSignatureGenerator extends Types.SignatureGenerator {
2407
2408 /**
2409 * An output buffer for type signatures.
2410 */
2411 StringBuilder sb = new StringBuilder();
2412
2413 /**
2414 * Are signatures incompatible with JVM spec allowed?
2415 * Used by {@link LambdaTranslationContext#serializedLambdaDisambiguation()}.
2416 */
2417 boolean allowIllegalSignatures;
2418
2419 L2MSignatureGenerator(boolean allowIllegalSignatures) {
2420 super(types);
2421 this.allowIllegalSignatures = allowIllegalSignatures;
2422 }
2423
2424 @Override
2425 protected void reportIllegalSignature(Type t) {
2426 if (!allowIllegalSignatures) {
2427 super.reportIllegalSignature(t);
2428 }
2429 }
2430
2431 @Override
2432 protected void append(char ch) {
2433 sb.append(ch);
2434 }
2435
2436 @Override
2437 protected void append(byte[] ba) {
2438 Name name = names.fromUtf(ba);
2439 sb.append(name.toString());
2440 }
2441
2442 @Override
2443 protected void append(Name name) {
2444 sb.append(name.toString());
2445 }
2446
2447 @Override
2448 public String toString() {
2449 return sb.toString();
2450 }
2451 }
2452 }