1 /*
2 * Copyright (c) 2010, 2025, 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.Attribute;
29 import com.sun.tools.javac.code.Flags;
30 import com.sun.tools.javac.code.Symbol;
31 import com.sun.tools.javac.code.Symbol.ClassSymbol;
32 import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol;
33 import com.sun.tools.javac.code.Symbol.MethodHandleSymbol;
34 import com.sun.tools.javac.code.Symbol.MethodSymbol;
35 import com.sun.tools.javac.code.Symbol.VarSymbol;
36 import com.sun.tools.javac.code.Symtab;
37 import com.sun.tools.javac.code.Type;
38 import com.sun.tools.javac.code.Type.MethodType;
39 import com.sun.tools.javac.code.Types;
40 import com.sun.tools.javac.code.Types.SignatureGenerator.InvalidSignatureException;
41 import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant;
42 import com.sun.tools.javac.main.Option;
43 import com.sun.tools.javac.resources.CompilerProperties.Errors;
44 import com.sun.tools.javac.resources.CompilerProperties.Fragments;
45 import com.sun.tools.javac.resources.CompilerProperties.Notes;
46 import com.sun.tools.javac.tree.JCTree;
47 import com.sun.tools.javac.tree.JCTree.JCAnnotation;
48 import com.sun.tools.javac.tree.JCTree.JCBinary;
49 import com.sun.tools.javac.tree.JCTree.JCBlock;
50 import com.sun.tools.javac.tree.JCTree.JCBreak;
51 import com.sun.tools.javac.tree.JCTree.JCCase;
52 import com.sun.tools.javac.tree.JCTree.JCClassDecl;
53 import com.sun.tools.javac.tree.JCTree.JCExpression;
54 import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
55 import com.sun.tools.javac.tree.JCTree.JCFunctionalExpression;
56 import com.sun.tools.javac.tree.JCTree.JCIdent;
57 import com.sun.tools.javac.tree.JCTree.JCLambda;
58 import com.sun.tools.javac.tree.JCTree.JCMemberReference;
59 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
60 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
61 import com.sun.tools.javac.tree.JCTree.JCNewClass;
62 import com.sun.tools.javac.tree.JCTree.JCReturn;
63 import com.sun.tools.javac.tree.JCTree.JCStatement;
64 import com.sun.tools.javac.tree.JCTree.JCSwitch;
65 import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
66 import com.sun.tools.javac.tree.JCTree.Tag;
67 import com.sun.tools.javac.tree.TreeInfo;
68 import com.sun.tools.javac.tree.TreeMaker;
69 import com.sun.tools.javac.tree.TreeTranslator;
70 import com.sun.tools.javac.util.Assert;
71 import com.sun.tools.javac.util.Context;
72 import com.sun.tools.javac.util.DiagnosticSource;
73 import com.sun.tools.javac.util.InvalidUtfException;
74 import com.sun.tools.javac.util.JCDiagnostic;
75 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
76 import com.sun.tools.javac.util.List;
77 import com.sun.tools.javac.util.ListBuffer;
78 import com.sun.tools.javac.util.Log;
79 import com.sun.tools.javac.util.Name;
80 import com.sun.tools.javac.util.Names;
81 import com.sun.tools.javac.util.Options;
82
83 import javax.lang.model.element.ElementKind;
84 import java.lang.invoke.LambdaMetafactory;
85 import java.util.HashMap;
86 import java.util.HashSet;
87 import java.util.Map;
88 import java.util.Set;
89 import java.util.function.Consumer;
90 import java.util.function.Supplier;
91
92 import static com.sun.tools.javac.code.Flags.ABSTRACT;
93 import static com.sun.tools.javac.code.Flags.BLOCK;
94 import static com.sun.tools.javac.code.Flags.DEFAULT;
95 import static com.sun.tools.javac.code.Flags.FINAL;
96 import static com.sun.tools.javac.code.Flags.INTERFACE;
97 import static com.sun.tools.javac.code.Flags.LAMBDA_METHOD;
98 import static com.sun.tools.javac.code.Flags.LOCAL_CAPTURE_FIELD;
99 import static com.sun.tools.javac.code.Flags.OUTER_THIS_FIELD;
100 import static com.sun.tools.javac.code.Flags.PARAMETER;
101 import static com.sun.tools.javac.code.Flags.PRIVATE;
102 import static com.sun.tools.javac.code.Flags.STATIC;
103 import static com.sun.tools.javac.code.Flags.STRICTFP;
104 import static com.sun.tools.javac.code.Flags.SYNTHETIC;
105 import static com.sun.tools.javac.code.Kinds.Kind.MTH;
106 import static com.sun.tools.javac.code.Kinds.Kind.TYP;
107 import static com.sun.tools.javac.code.Kinds.Kind.VAR;
108 import static com.sun.tools.javac.code.TypeTag.BOT;
109 import static com.sun.tools.javac.code.TypeTag.VOID;
110 import com.sun.tools.javac.jvm.Target;
111 import com.sun.tools.javac.tree.JCTree.JCThrow;
112
113 /**
114 * This pass desugars lambda expressions into static methods
115 *
116 * <p><b>This is NOT part of any supported API.
117 * If you write code that depends on this, you do so at your own risk.
118 * This code and its internal interfaces are subject to change or
119 * deletion without notice.</b>
120 */
121 public class LambdaToMethod extends TreeTranslator {
122
123 private final Attr attr;
124 private final JCDiagnostic.Factory diags;
125 private final Log log;
126 private final Lower lower;
127 private final Names names;
128 private final Symtab syms;
129 private final Resolve rs;
130 private final Operators operators;
131 private TreeMaker make;
132 private final Types types;
133 private final TransTypes transTypes;
134 private final Target target;
135 private Env<AttrContext> attrEnv;
136
137 /** info about the current class being processed */
138 private KlassInfo kInfo;
139
140 /** translation context of the current lambda expression */
141 private LambdaTranslationContext lambdaContext;
142
143 /** the variable whose initializer is pending */
144 private VarSymbol pendingVar;
145
146 /** dump statistics about lambda code generation */
147 private final boolean dumpLambdaToMethodStats;
148
149 /** dump statistics about lambda deserialization code generation */
150 private final boolean dumpLambdaDeserializationStats;
151
152 /** force serializable representation, for stress testing **/
153 private final boolean forceSerializable;
154
155 /** true if line or local variable debug info has been requested */
156 private final boolean debugLinesOrVars;
157
158 /** dump statistics about lambda method deduplication */
159 private final boolean verboseDeduplication;
160
161 /** deduplicate lambda implementation methods */
162 private final boolean deduplicateLambdas;
163
164 /** Flag for alternate metafactories indicating the lambda object is intended to be serializable */
165 public static final int FLAG_SERIALIZABLE = LambdaMetafactory.FLAG_SERIALIZABLE;
166
167 /** Flag for alternate metafactories indicating the lambda object has multiple targets */
168 public static final int FLAG_MARKERS = LambdaMetafactory.FLAG_MARKERS;
169
170 /** Flag for alternate metafactories indicating the lambda object requires multiple bridges */
171 public static final int FLAG_BRIDGES = LambdaMetafactory.FLAG_BRIDGES;
172
173 // <editor-fold defaultstate="collapsed" desc="Instantiating">
174 protected static final Context.Key<LambdaToMethod> unlambdaKey = new Context.Key<>();
175
176 public static LambdaToMethod instance(Context context) {
177 LambdaToMethod instance = context.get(unlambdaKey);
178 if (instance == null) {
179 instance = new LambdaToMethod(context);
180 }
181 return instance;
182 }
183 private LambdaToMethod(Context context) {
184 context.put(unlambdaKey, this);
185 diags = JCDiagnostic.Factory.instance(context);
186 log = Log.instance(context);
187 lower = Lower.instance(context);
188 names = Names.instance(context);
189 syms = Symtab.instance(context);
190 rs = Resolve.instance(context);
191 operators = Operators.instance(context);
192 make = TreeMaker.instance(context);
193 types = Types.instance(context);
194 transTypes = TransTypes.instance(context);
195 target = Target.instance(context);
196 Options options = Options.instance(context);
197 dumpLambdaToMethodStats = options.isSet("debug.dumpLambdaToMethodStats");
198 dumpLambdaDeserializationStats = options.isSet("debug.dumpLambdaDeserializationStats");
199 attr = Attr.instance(context);
200 forceSerializable = options.isSet("forceSerializable");
201 boolean lineDebugInfo =
202 options.isUnset(Option.G_CUSTOM) ||
203 options.isSet(Option.G_CUSTOM, "lines");
204 boolean varDebugInfo =
205 options.isUnset(Option.G_CUSTOM)
206 ? options.isSet(Option.G)
207 : options.isSet(Option.G_CUSTOM, "vars");
208 debugLinesOrVars = lineDebugInfo || varDebugInfo;
209 verboseDeduplication = options.isSet("debug.dumpLambdaToMethodDeduplication");
210 deduplicateLambdas = options.getBoolean("deduplicateLambdas", true);
211 }
212 // </editor-fold>
213
214 class DedupedLambda {
215 private final MethodSymbol symbol;
216 private final JCTree tree;
217
218 private int hashCode;
219
220 DedupedLambda(MethodSymbol symbol, JCTree tree) {
221 this.symbol = symbol;
222 this.tree = tree;
223 }
224
225 @Override
226 public int hashCode() {
227 int hashCode = this.hashCode;
228 if (hashCode == 0) {
229 this.hashCode = hashCode = TreeHasher.hash(types, tree, symbol.params());
230 }
231 return hashCode;
232 }
233
234 @Override
235 public boolean equals(Object o) {
236 return (o instanceof DedupedLambda dedupedLambda)
237 && types.isSameType(symbol.asType(), dedupedLambda.symbol.asType())
238 && new TreeDiffer(types, symbol.params(), dedupedLambda.symbol.params()).scan(tree, dedupedLambda.tree);
239 }
240 }
241
242 private class KlassInfo {
243
244 /**
245 * list of methods to append
246 */
247 private ListBuffer<JCTree> appendedMethodList = new ListBuffer<>();
248
249 private final Map<DedupedLambda, DedupedLambda> dedupedLambdas = new HashMap<>();
250
251 private final Map<Object, DynamicMethodSymbol> dynMethSyms = new HashMap<>();
252
253 /**
254 * list of deserialization cases
255 */
256 private final Map<String, DeserializationCase> deserializeCases = new HashMap<>();
257
258 /**
259 * deserialize method symbol
260 */
261 private final MethodSymbol deserMethodSym;
262
263 /**
264 * deserialize method parameter symbol
265 */
266 private final VarSymbol deserParamSym;
267
268 private final JCClassDecl clazz;
269
270 private final Map<String, Integer> syntheticNames = new HashMap<>();
271
272 private KlassInfo(JCClassDecl clazz) {
273 this.clazz = clazz;
274 MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType,
275 List.nil(), syms.methodClass);
276 deserMethodSym = makePrivateSyntheticMethod(STATIC, names.deserializeLambda, type, clazz.sym);
277 deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"),
278 syms.serializedLambdaType, deserMethodSym);
279 }
280
281 private void addMethod(JCTree decl) {
282 appendedMethodList = appendedMethodList.prepend(decl);
283 }
284
285 int syntheticNameIndex(StringBuilder buf, int start) {
286 String temp = buf.toString();
287 Integer count = syntheticNames.get(temp);
288 if (count == null) {
289 count = start;
290 }
291 syntheticNames.put(temp, count + 1);
292 return count;
293 }
294 }
295
296 // <editor-fold defaultstate="collapsed" desc="visitor methods">
297 public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
298 this.make = make;
299 this.attrEnv = env;
300 return translate(cdef);
301 }
302
303 /**
304 * Visit a class.
305 * Maintain the translatedMethodList across nested classes.
306 * Append the translatedMethodList to the class after it is translated.
307 */
308 @Override
309 public void visitClassDef(JCClassDecl tree) {
310 KlassInfo prevKlassInfo = kInfo;
311 DiagnosticSource prevSource = log.currentSource();
312 LambdaTranslationContext prevLambdaContext = lambdaContext;
313 VarSymbol prevPendingVar = pendingVar;
314 try {
315 kInfo = new KlassInfo(tree);
316 log.useSource(tree.sym.sourcefile);
317 lambdaContext = null;
318 pendingVar = null;
319 super.visitClassDef(tree);
320 if (prevLambdaContext != null) {
321 tree.sym.owner = prevLambdaContext.translatedSym;
322 }
323 if (!kInfo.deserializeCases.isEmpty()) {
324 int prevPos = make.pos;
325 try {
326 make.at(tree);
327 makeDeserializeMethod().forEach(kInfo::addMethod);
328 } finally {
329 make.at(prevPos);
330 }
331 }
332 //add all translated instance methods here
333 List<JCTree> newMethods = kInfo.appendedMethodList.toList();
334 tree.defs = tree.defs.appendList(newMethods);
335 for (JCTree lambda : newMethods) {
336 tree.sym.members().enter(((JCMethodDecl)lambda).sym);
337 }
338 result = tree;
339 } finally {
340 kInfo = prevKlassInfo;
341 log.useSource(prevSource.getFile());
342 lambdaContext = prevLambdaContext;
343 pendingVar = prevPendingVar;
344 }
345 }
346
347 /**
348 * Translate a lambda into a method to be inserted into the class.
349 * Then replace the lambda site with an invokedynamic call of to lambda
350 * meta-factory, which will use the lambda method.
351 */
352 @Override
353 public void visitLambda(JCLambda tree) {
354 LambdaTranslationContext localContext = new LambdaTranslationContext(tree);
355 MethodSymbol sym = localContext.translatedSym;
356 MethodType lambdaType = (MethodType) sym.type;
357
358 { /* Type annotation management: Based on where the lambda features, type annotations that
359 are interior to it, may at this point be attached to the enclosing method, or the first
360 constructor in the class, or in the enclosing class symbol or in the field whose
361 initializer is the lambda. In any event, gather up the annotations that belong to the
362 lambda and attach it to the implementation method.
363 */
364
365 Symbol owner = tree.owner;
366 apportionTypeAnnotations(tree,
367 owner::getRawTypeAttributes,
368 owner::setTypeAttributes,
369 sym::setTypeAttributes);
370
371 final long ownerFlags = owner.flags();
372 if ((ownerFlags & Flags.BLOCK) != 0) {
373 ClassSymbol cs = (ClassSymbol) owner.owner;
374 boolean isStaticInit = (ownerFlags & Flags.STATIC) != 0;
375 apportionTypeAnnotations(tree,
376 isStaticInit ? cs::getClassInitTypeAttributes : cs::getInitTypeAttributes,
377 isStaticInit ? cs::setClassInitTypeAttributes : cs::setInitTypeAttributes,
378 sym::appendUniqueTypeAttributes);
379 }
380
381 if (pendingVar != null && pendingVar.getKind() == ElementKind.FIELD) {
382 apportionTypeAnnotations(tree,
383 pendingVar::getRawTypeAttributes,
384 pendingVar::setTypeAttributes,
385 sym::appendUniqueTypeAttributes);
386 }
387 }
388
389 //create the method declaration hoisting the lambda body
390 JCMethodDecl lambdaDecl = make.MethodDef(make.Modifiers(sym.flags_field),
391 sym.name,
392 make.QualIdent(lambdaType.getReturnType().tsym),
393 List.nil(),
394 localContext.syntheticParams,
395 lambdaType.getThrownTypes() == null ?
396 List.nil() :
397 make.Types(lambdaType.getThrownTypes()),
398 null,
399 null);
400 lambdaDecl.sym = sym;
401 lambdaDecl.type = lambdaType;
402
403 //now that we have generated a method for the lambda expression,
404 //we can translate the lambda into a method reference pointing to the newly
405 //created method.
406 //
407 //Note that we need to adjust the method handle so that it will match the
408 //signature of the SAM descriptor - this means that the method reference
409 //should be added the following synthetic arguments:
410 //
411 // * the "this" argument if it is an instance method
412 // * enclosing locals captured by the lambda expression
413
414 ListBuffer<JCExpression> syntheticInits = new ListBuffer<>();
415
416 if (!sym.isStatic()) {
417 syntheticInits.append(makeThis(
418 sym.owner.enclClass().asType(),
419 tree.owner.enclClass()));
420 }
421
422 //add captured locals
423 for (Symbol fv : localContext.capturedVars) {
424 JCExpression captured_local = make.Ident(fv).setType(fv.type);
425 syntheticInits.append(captured_local);
426 }
427
428 //then, determine the arguments to the indy call
429 List<JCExpression> indy_args = translate(syntheticInits.toList());
430
431 LambdaTranslationContext prevLambdaContext = lambdaContext;
432 try {
433 lambdaContext = localContext;
434 //translate lambda body
435 //As the lambda body is translated, all references to lambda locals,
436 //captured variables, enclosing members are adjusted accordingly
437 //to refer to the static method parameters (rather than i.e. accessing
438 //captured members directly).
439 lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl));
440 } finally {
441 lambdaContext = prevLambdaContext;
442 }
443
444 boolean dedupe = false;
445 if (deduplicateLambdas && !debugLinesOrVars && !isSerializable(tree)) {
446 DedupedLambda dedupedLambda = new DedupedLambda(lambdaDecl.sym, lambdaDecl.body);
447 DedupedLambda existing = kInfo.dedupedLambdas.putIfAbsent(dedupedLambda, dedupedLambda);
448 if (existing != null) {
449 sym = existing.symbol;
450 dedupe = true;
451 if (verboseDeduplication) log.note(tree, Notes.VerboseL2mDeduplicate(sym));
452 }
453 }
454 if (!dedupe) {
455 //Add the method to the list of methods to be added to this class.
456 kInfo.addMethod(lambdaDecl);
457 }
458
459 //convert to an invokedynamic call
460 result = makeMetafactoryIndyCall(tree, sym.asHandle(), localContext.translatedSym, indy_args);
461 }
462
463 // where
464 // Reassign type annotations from the source that should really belong to the lambda
465 private void apportionTypeAnnotations(JCLambda tree,
466 Supplier<List<Attribute.TypeCompound>> source,
467 Consumer<List<Attribute.TypeCompound>> owner,
468 Consumer<List<Attribute.TypeCompound>> lambda) {
469
470 ListBuffer<Attribute.TypeCompound> ownerTypeAnnos = new ListBuffer<>();
471 ListBuffer<Attribute.TypeCompound> lambdaTypeAnnos = new ListBuffer<>();
472
473 for (Attribute.TypeCompound tc : source.get()) {
474 if (tc.hasUnknownPosition()) {
475 // Handle container annotations
476 tc.tryFixPosition();
477 }
478 if (tc.position.onLambda == tree) {
479 lambdaTypeAnnos.append(tc);
480 } else {
481 ownerTypeAnnos.append(tc);
482 }
483 }
484 if (lambdaTypeAnnos.nonEmpty()) {
485 owner.accept(ownerTypeAnnos.toList());
486 lambda.accept(lambdaTypeAnnos.toList());
487 }
488 }
489
490 private JCIdent makeThis(Type type, Symbol owner) {
491 VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC,
492 names._this,
493 type,
494 owner);
495 return make.Ident(_this);
496 }
497
498 /**
499 * Translate a method reference into an invokedynamic call to the
500 * meta-factory.
501 */
502 @Override
503 public void visitReference(JCMemberReference tree) {
504 //first determine the method symbol to be used to generate the sam instance
505 //this is either the method reference symbol, or the bridged reference symbol
506 MethodSymbol refSym = (MethodSymbol)tree.sym;
507
508 //the qualifying expression is treated as a special captured arg
509 JCExpression init = switch (tree.kind) {
510 case IMPLICIT_INNER, /* Inner :: new */
511 SUPER -> /* super :: instMethod */
512 makeThis(tree.owner.enclClass().asType(), tree.owner.enclClass());
513 case BOUND -> /* Expr :: instMethod */
514 attr.makeNullCheck(transTypes.coerce(attrEnv, tree.getQualifierExpression(),
515 types.erasure(tree.sym.owner.type)));
516 case UNBOUND, /* Type :: instMethod */
517 STATIC, /* Type :: staticMethod */
518 TOPLEVEL, /* Top level :: new */
519 ARRAY_CTOR -> /* ArrayType :: new */
520 null;
521 };
522
523 List<JCExpression> indy_args = (init == null) ?
524 List.nil() : translate(List.of(init));
525
526 //build a sam instance using an indy call to the meta-factory
527 result = makeMetafactoryIndyCall(tree, refSym.asHandle(), refSym, indy_args);
528 }
529
530 /**
531 * Translate identifiers within a lambda to the mapped identifier
532 */
533 @Override
534 public void visitIdent(JCIdent tree) {
535 if (lambdaContext == null) {
536 super.visitIdent(tree);
537 } else {
538 int prevPos = make.pos;
539 try {
540 make.at(tree);
541 JCTree ltree = lambdaContext.translate(tree);
542 if (ltree != null) {
543 result = ltree;
544 } else {
545 //access to untranslated symbols (i.e. compile-time constants,
546 //members defined inside the lambda body, etc.) )
547 super.visitIdent(tree);
548 }
549 } finally {
550 make.at(prevPos);
551 }
552 }
553 }
554
555 @Override
556 public void visitVarDef(JCVariableDecl tree) {
557 VarSymbol prevPendingVar = pendingVar;
558 try {
559 pendingVar = tree.sym;
560 if (lambdaContext != null) {
561 tree.sym = lambdaContext.addLocal(tree.sym);
562 tree.init = translate(tree.init);
563 result = tree;
564 } else {
565 super.visitVarDef(tree);
566 }
567 } finally {
568 pendingVar = prevPendingVar;
569 }
570 }
571
572 // </editor-fold>
573
574 // <editor-fold defaultstate="collapsed" desc="Translation helper methods">
575
576 private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) {
577 return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ?
578 makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) :
579 makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally);
580 }
581
582 private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) {
583 Type restype = lambdaMethodDecl.type.getReturnType();
584 boolean isLambda_void = expr.type.hasTag(VOID);
585 boolean isTarget_void = restype.hasTag(VOID);
586 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
587 int prevPos = make.pos;
588 try {
589 if (isTarget_void) {
590 //target is void:
591 // BODY;
592 JCStatement stat = make.at(expr).Exec(expr);
593 return make.Block(0, List.of(stat));
594 } else if (isLambda_void && isTarget_Void) {
595 //void to Void conversion:
596 // BODY; return null;
597 ListBuffer<JCStatement> stats = new ListBuffer<>();
598 stats.append(make.at(expr).Exec(expr));
599 stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
600 return make.Block(0, stats.toList());
601 } else {
602 //non-void to non-void conversion:
603 // return BODY;
604 return make.at(expr).Block(0, List.of(make.Return(expr)));
605 }
606 } finally {
607 make.at(prevPos);
608 }
609 }
610
611 private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) {
612 final Type restype = lambdaMethodDecl.type.getReturnType();
613 final boolean isTarget_void = restype.hasTag(VOID);
614 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
615
616 class LambdaBodyTranslator extends TreeTranslator {
617
618 @Override
619 public void visitClassDef(JCClassDecl tree) {
620 //do NOT recurse on any inner classes
621 result = tree;
622 }
623
624 @Override
625 public void visitLambda(JCLambda tree) {
626 //do NOT recurse on any nested lambdas
627 result = tree;
628 }
629
630 @Override
631 public void visitReturn(JCReturn tree) {
632 boolean isLambda_void = tree.expr == null;
633 if (isTarget_void && !isLambda_void) {
634 //Void to void conversion:
635 // { TYPE $loc = RET-EXPR; return; }
636 VarSymbol loc = new VarSymbol(SYNTHETIC, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym);
637 JCVariableDecl varDef = make.VarDef(loc, tree.expr);
638 result = make.Block(0, List.of(varDef, make.Return(null)));
639 } else {
640 result = tree;
641 }
642
643 }
644 }
645
646 JCBlock trans_block = new LambdaBodyTranslator().translate(block);
647 if (completeNormally && isTarget_Void) {
648 //there's no return statement and the lambda (possibly inferred)
649 //return type is java.lang.Void; emit a synthetic return statement
650 trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
651 }
652 return trans_block;
653 }
654
655 // When an instance created for a "lambda" is serialized, the type that is
656 // serialized is java.lang.invoke.SerializedLambda.
657 // Its SerializedLambda.readResolve will call method $deserializeLambda$
658 // on the class containing the lambda, passing the SerializedLambda as
659 // a parameter. The $deserializeLambda$ is responsible for recreating the
660 // appropriate instance.
661 //
662 // The $deserializeLambda$ looks like this:
663 // private static Object $deserializeLambda$(final java.lang.invoke.SerializedLambda lambda) {
664 // switch (lambda.getImplMethodName()) {
665 // case <implMethodName> -> return $deserializeLambda$<implMethodName>(lambda);
666 // }
667 // throw new IllegalArgumentException("Invalid lambda deserialization");
668 // }
669 //
670 // The $deserializeLambda$<implMethodName> methods then look like:
671 // private static Object $deserializeLambda$<implMethodName>(final java.lang.invoke.SerializedLambda lambda) {
672 // if (lambda.getImplMethodKind() == ... &&
673 // lambda.getFunctionalInterfaceClass().equals(...) &&
674 // lambda.getFunctionalInterfaceMethodName().equals(...) &&
675 // lambda.getFunctionalInterfaceMethodSignature().equals(...) &&
676 // lambda.getImplClass().equals(...) &&
677 // lambda.getImplMethodSignature().equals(...) &&
678 // lambda.getInstantiatedMethodType().equals(...)) return <recreate-lambda>;
679 // //any additional deserialization cases with the same implMethodName.
680 // throw new IllegalArgumentException("Invalid lambda deserialization");
681 // }
682 //
683 // The $deserializeLambda$<implMethodName> may contain multiple if statements if
684 // there are multiple SerializedLambdas with the same implMethodName name.
685 // This may happen when a method references is serialized.
686 private List<JCMethodDecl> makeDeserializeMethod() {
687 ListBuffer<JCCase> cases = new ListBuffer<>();
688 ListBuffer<JCBreak> breaks = new ListBuffer<>();
689 ListBuffer<JCMethodDecl> deserializeMethods = new ListBuffer<>();
690 for (Map.Entry<String, DeserializationCase> entry : kInfo.deserializeCases.entrySet()) {
691 deserializeMethods.append(createImplementationNameDeserializationMethod(entry.getValue()));
692
693 JCBreak br = make.Break(null);
694 breaks.add(br);
695 List<JCStatement> stmts = List.of(
696 make.Return(make.App(make.QualIdent(entry.getValue().deserializationMethod), List.of(make.Ident(kInfo.deserParamSym)))),
697 br
698 );
699 cases.add(make.Case(JCCase.STATEMENT, List.of(make.ConstantCaseLabel(make.Literal(entry.getKey()))), null, stmts, null));
700 }
701 JCSwitch sw = make.Switch(deserGetter(kInfo.deserParamSym, "getImplMethodName", syms.stringType), cases.toList());
702 for (JCBreak br : breaks) {
703 br.target = sw;
704 }
705 JCBlock body = make.Block(0L, List.of(
706 sw,
707 createThrowInvalidLambdaDeserialization()));
708 JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()),
709 names.deserializeLambda,
710 make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym),
711 List.nil(),
712 List.of(make.VarDef(kInfo.deserParamSym, null)),
713 List.nil(),
714 body,
715 null);
716 deser.sym = kInfo.deserMethodSym;
717 deser.type = kInfo.deserMethodSym.type;
718 //System.err.printf("DESER: '%s'\n", deser);
719 deserializeMethods.append(lower.translateMethod(attrEnv, deser, make));
720 return deserializeMethods.toList();
721 }
722
723 private JCThrow createThrowInvalidLambdaDeserialization() {
724 return make.Throw(makeNewClass(
725 syms.illegalArgumentExceptionType,
726 List.of(make.Literal("Invalid lambda deserialization"))));
727 }
728
729 private JCMethodDecl createImplementationNameDeserializationMethod(DeserializationCase deserializationCase) {
730 JCBlock body = make.Block(0L,
731 deserializationCase.stmts
732 .append(createThrowInvalidLambdaDeserialization())
733 .toList());
734 JCMethodDecl deser = make.MethodDef(make.Modifiers(deserializationCase.deserializationMethod().flags()),
735 deserializationCase.deserializationMethod().name,
736 make.QualIdent(deserializationCase.deserializationMethod().getReturnType().tsym),
737 List.nil(),
738 List.of(make.VarDef(deserializationCase.deserParamSym(), null)),
739 List.nil(),
740 body,
741 null);
742 deser.sym = deserializationCase.deserializationMethod();
743 deser.type = deserializationCase.deserializationMethod().type;
744 //System.err.printf("DESER: '%s'\n", deser);
745 return lower.translateMethod(attrEnv, deser, make);
746 }
747
748 /** Make an attributed class instance creation expression.
749 * @param ctype The class type.
750 * @param args The constructor arguments.
751 * @param cons The constructor symbol
752 */
753 JCNewClass makeNewClass(Type ctype, List<JCExpression> args, Symbol cons) {
754 JCNewClass tree = make.NewClass(null,
755 null, make.QualIdent(ctype.tsym), args, null);
756 tree.constructor = cons;
757 tree.type = ctype;
758 return tree;
759 }
760
761 /** Make an attributed class instance creation expression.
762 * @param ctype The class type.
763 * @param args The constructor arguments.
764 */
765 JCNewClass makeNewClass(Type ctype, List<JCExpression> args) {
766 return makeNewClass(ctype, args,
767 rs.resolveConstructor(null, attrEnv, ctype, TreeInfo.types(args), List.nil()));
768 }
769
770 private void addDeserializationCase(MethodHandleSymbol refSym, Type targetType, MethodSymbol samSym, Type samType,
771 DiagnosticPosition pos, List<LoadableConstant> staticArgs, MethodType indyType) {
772 String functionalInterfaceClass = classSig(targetType);
773 String functionalInterfaceMethodName = samSym.getSimpleName().toString();
774 String functionalInterfaceMethodSignature = typeSig(types.erasure(samSym.type));
775 if (refSym.enclClass().isInterface()) {
776 Symbol baseMethod = types.overriddenObjectMethod(refSym.enclClass(), refSym);
777 if (baseMethod != null) {
778 // The implementation method is a java.lang.Object method, runtime will resolve this method to
779 // a java.lang.Object method, so do the same.
780 // This case can be removed if JDK-8172817 is fixed.
781 refSym = ((MethodSymbol) baseMethod).asHandle();
782 }
783 }
784 String implClass = classSig(types.erasure(refSym.owner.type));
785 Name implMethodNameAsName = refSym.getQualifiedName();
786 String implMethodName = implMethodNameAsName.toString();
787 String implMethodSignature = typeSig(types.erasure(refSym.type));
788 String instantiatedMethodType = typeSig(types.erasure(samType));
789
790 int implMethodKind = refSym.referenceKind();
791
792 DeserializationCase deserializationCase = kInfo.deserializeCases.computeIfAbsent(implMethodName, _ -> {
793 Name currentDeserializationMethodName = implMethodNameAsName == names.init
794 ? names.deserializeLambda.append(names.fromString("init"))
795 : names.deserializeLambda.append(target.syntheticNameChar(), implMethodNameAsName);
796 MethodSymbol caseDeserializationMethod = makePrivateSyntheticMethod(STATIC, currentDeserializationMethodName,
797 kInfo.deserMethodSym.type, kInfo.clazz.sym);
798 VarSymbol caseDeserializationParam = new VarSymbol(FINAL, names.fromString("lambda"),
799 syms.serializedLambdaType, caseDeserializationMethod);
800 return new DeserializationCase(caseDeserializationMethod, caseDeserializationParam, new ListBuffer<>());
801 });
802 VarSymbol deserParamSym = deserializationCase.deserParamSym();
803
804 JCExpression kindTest = eqTest(syms.intType, deserGetter(deserParamSym, "getImplMethodKind", syms.intType),
805 make.Literal(implMethodKind));
806 ListBuffer<JCExpression> serArgs = new ListBuffer<>();
807 int i = 0;
808 for (Type t : indyType.getParameterTypes()) {
809 List<JCExpression> indexAsArg = new ListBuffer<JCExpression>().append(make.Literal(i)).toList();
810 List<Type> argTypes = new ListBuffer<Type>().append(syms.intType).toList();
811 serArgs.add(make.TypeCast(types.erasure(t), deserGetter(deserParamSym, "getCapturedArg", syms.objectType, argTypes, indexAsArg)));
812 ++i;
813 }
814 JCStatement stmt = make.If(
815 deserTest(deserParamSym,
816 deserTest(deserParamSym,
817 deserTest(deserParamSym,
818 deserTest(deserParamSym,
819 deserTest(deserParamSym,
820 deserTest(deserParamSym,
821 kindTest,
822 "getFunctionalInterfaceClass", functionalInterfaceClass),
823 "getFunctionalInterfaceMethodName", functionalInterfaceMethodName),
824 "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature),
825 "getImplClass", implClass),
826 "getImplMethodSignature", implMethodSignature),
827 "getInstantiatedMethodType", instantiatedMethodType),
828 make.Return(makeIndyCall(
829 pos,
830 syms.lambdaMetafactory,
831 names.altMetafactory,
832 staticArgs, indyType, serArgs.toList(), samSym.name)),
833 null);
834 if (dumpLambdaDeserializationStats) {
835 log.note(pos, Notes.LambdaDeserializationStat(
836 functionalInterfaceClass,
837 functionalInterfaceMethodName,
838 functionalInterfaceMethodSignature,
839 implMethodKind,
840 implClass,
841 implMethodName,
842 implMethodSignature,
843 instantiatedMethodType));
844 }
845 deserializationCase.stmts().append(stmt);
846 }
847
848 private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) {
849 JCBinary testExpr = make.Binary(Tag.EQ, arg1, arg2);
850 testExpr.operator = operators.resolveBinary(testExpr, Tag.EQ, argType, argType);
851 testExpr.setType(syms.booleanType);
852 return testExpr;
853 }
854
855 private JCExpression deserTest(VarSymbol deserParamSym, JCExpression prev, String func, String lit) {
856 MethodType eqmt = new MethodType(List.of(syms.objectType), syms.booleanType, List.nil(), syms.methodClass);
857 Symbol eqsym = rs.resolveQualifiedMethod(null, attrEnv, syms.objectType, names.equals, List.of(syms.objectType), List.nil());
858 JCMethodInvocation eqtest = make.Apply(
859 List.nil(),
860 make.Select(deserGetter(deserParamSym, func, syms.stringType), eqsym).setType(eqmt),
861 List.of(make.Literal(lit)));
862 eqtest.setType(syms.booleanType);
863 JCBinary compound = make.Binary(Tag.AND, prev, eqtest);
864 compound.operator = operators.resolveBinary(compound, Tag.AND, syms.booleanType, syms.booleanType);
865 compound.setType(syms.booleanType);
866 return compound;
867 }
868
869 private JCExpression deserGetter(VarSymbol deserParamSym, String func, Type type) {
870 return deserGetter(deserParamSym, func, type, List.nil(), List.nil());
871 }
872
873 private JCExpression deserGetter(VarSymbol deserParamSym, String func, Type type, List<Type> argTypes, List<JCExpression> args) {
874 MethodType getmt = new MethodType(argTypes, type, List.nil(), syms.methodClass);
875 Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.nil());
876 return make.Apply(
877 List.nil(),
878 make.Select(make.Ident(deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt),
879 args).setType(type);
880 }
881
882 /**
883 * Create new synthetic method with given flags, name, type, owner
884 */
885 private MethodSymbol makePrivateSyntheticMethod(long flags, Name name, Type type, Symbol owner) {
886 return new MethodSymbol(flags | SYNTHETIC | PRIVATE, name, type, owner);
887 }
888
889 private MethodType typeToMethodType(Type mt) {
890 Type type = types.erasure(mt);
891 return new MethodType(type.getParameterTypes(),
892 type.getReturnType(),
893 type.getThrownTypes(),
894 syms.methodClass);
895 }
896
897 /**
898 * Generate an indy method call to the meta factory
899 */
900 private JCExpression makeMetafactoryIndyCall(JCFunctionalExpression tree,
901 MethodHandleSymbol refSym, MethodSymbol nonDedupedRefSym,
902 List<JCExpression> indy_args) {
903 //determine the static bsm args
904 MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.target.tsym);
905 MethodType samType = typeToMethodType(tree.getDescriptorType(types));
906 List<LoadableConstant> staticArgs = List.of(
907 typeToMethodType(samSym.type),
908 refSym.asHandle(),
909 samType);
910
911 //computed indy arg types
912 ListBuffer<Type> indy_args_types = new ListBuffer<>();
913 for (JCExpression arg : indy_args) {
914 indy_args_types.append(arg.type);
915 }
916
917 //finally, compute the type of the indy call
918 MethodType indyType = new MethodType(indy_args_types.toList(),
919 tree.type,
920 List.nil(),
921 syms.methodClass);
922
923 List<Symbol> bridges = bridges(tree);
924 boolean isSerializable = isSerializable(tree);
925 boolean needsAltMetafactory = tree.target.isIntersection() ||
926 isSerializable || bridges.length() > 1;
927
928 dumpStats(tree, needsAltMetafactory, nonDedupedRefSym);
929
930 Name metafactoryName = needsAltMetafactory ?
931 names.altMetafactory : names.metafactory;
932
933 if (needsAltMetafactory) {
934 ListBuffer<Type> markers = new ListBuffer<>();
935 List<Type> targets = tree.target.isIntersection() ?
936 types.directSupertypes(tree.target) :
937 List.nil();
938 for (Type t : targets) {
939 t = types.erasure(t);
940 if (t.tsym != syms.serializableType.tsym &&
941 t.tsym != tree.type.tsym &&
942 t.tsym != syms.objectType.tsym) {
943 markers.append(t);
944 }
945 }
946 int flags = isSerializable ? FLAG_SERIALIZABLE : 0;
947 boolean hasMarkers = markers.nonEmpty();
948 boolean hasBridges = bridges.nonEmpty();
949 if (hasMarkers) {
950 flags |= FLAG_MARKERS;
951 }
952 if (hasBridges) {
953 flags |= FLAG_BRIDGES;
954 }
955 staticArgs = staticArgs.append(LoadableConstant.Int(flags));
956 if (hasMarkers) {
957 staticArgs = staticArgs.append(LoadableConstant.Int(markers.length()));
958 staticArgs = staticArgs.appendList(List.convert(LoadableConstant.class, markers.toList()));
959 }
960 if (hasBridges) {
961 staticArgs = staticArgs.append(LoadableConstant.Int(bridges.length() - 1));
962 for (Symbol s : bridges) {
963 Type s_erasure = s.erasure(types);
964 if (!types.isSameType(s_erasure, samSym.erasure(types))) {
965 staticArgs = staticArgs.append(((MethodType)s.erasure(types)));
966 }
967 }
968 }
969 if (isSerializable) {
970 int prevPos = make.pos;
971 try {
972 make.at(kInfo.clazz);
973 addDeserializationCase(refSym, tree.type, samSym, samType,
974 tree, staticArgs, indyType);
975 } finally {
976 make.at(prevPos);
977 }
978 }
979 }
980
981 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args, samSym.name);
982 }
983
984 /**
985 * Generate an indy method call with given name, type and static bootstrap
986 * arguments types
987 */
988 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
989 List<LoadableConstant> staticArgs, MethodType indyType, List<JCExpression> indyArgs,
990 Name methName) {
991 int prevPos = make.pos;
992 try {
993 make.at(pos);
994 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
995 syms.stringType,
996 syms.methodTypeType).appendList(staticArgs.map(types::constantType));
997
998 MethodSymbol bsm = rs.resolveInternalMethod(pos, attrEnv, site,
999 bsmName, bsm_staticArgs, List.nil());
1000
1001 DynamicMethodSymbol dynSym =
1002 new DynamicMethodSymbol(methName,
1003 syms.noSymbol,
1004 bsm.asHandle(),
1005 indyType,
1006 staticArgs.toArray(new LoadableConstant[staticArgs.length()]));
1007 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName);
1008 DynamicMethodSymbol existing = kInfo.dynMethSyms.putIfAbsent(
1009 dynSym.poolKey(types), dynSym);
1010 qualifier.sym = existing != null ? existing : dynSym;
1011 qualifier.type = indyType.getReturnType();
1012
1013 JCMethodInvocation proxyCall = make.Apply(List.nil(), qualifier, indyArgs);
1014 proxyCall.type = indyType.getReturnType();
1015 return proxyCall;
1016 } finally {
1017 make.at(prevPos);
1018 }
1019 }
1020
1021 List<Symbol> bridges(JCFunctionalExpression tree) {
1022 ClassSymbol csym =
1023 types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.target, ABSTRACT | INTERFACE);
1024 return types.functionalInterfaceBridges(csym);
1025 }
1026
1027 /** does this functional expression require serialization support? */
1028 boolean isSerializable(JCFunctionalExpression tree) {
1029 if (forceSerializable) {
1030 return true;
1031 }
1032 return types.asSuper(tree.target, syms.serializableType.tsym) != null;
1033 }
1034
1035 void dumpStats(JCFunctionalExpression tree, boolean needsAltMetafactory, Symbol sym) {
1036 if (dumpLambdaToMethodStats) {
1037 if (tree instanceof JCLambda lambda) {
1038 log.note(tree, diags.noteKey(lambda.wasMethodReference ? "mref.stat.1" : "lambda.stat",
1039 needsAltMetafactory, sym));
1040 } else if (tree instanceof JCMemberReference) {
1041 log.note(tree, Notes.MrefStat(needsAltMetafactory, null));
1042 }
1043 }
1044 }
1045
1046 /**
1047 * This class retains all the useful information about a lambda expression,
1048 * and acts as a translation map that is used by the main translation routines
1049 * in order to adjust references to captured locals/members, etc.
1050 */
1051 class LambdaTranslationContext {
1052
1053 /** the underlying (untranslated) tree */
1054 final JCFunctionalExpression tree;
1055
1056 /** a translation map from source symbols to translated symbols */
1057 final Map<VarSymbol, VarSymbol> lambdaProxies = new HashMap<>();
1058
1059 /** the list of symbols captured by this lambda expression */
1060 final List<VarSymbol> capturedVars;
1061
1062 /** the synthetic symbol for the method hoisting the translated lambda */
1063 final MethodSymbol translatedSym;
1064
1065 /** the list of parameter declarations of the translated lambda method */
1066 final List<JCVariableDecl> syntheticParams;
1067
1068 LambdaTranslationContext(JCLambda tree) {
1069 this.tree = tree;
1070 // This symbol will be filled-in in complete
1071 Symbol owner = tree.owner;
1072 if (owner.kind == MTH) {
1073 final MethodSymbol originalOwner = (MethodSymbol)owner.clone(owner.owner);
1074 this.translatedSym = new MethodSymbol(0, null, null, owner.enclClass()) {
1075 @Override
1076 public MethodSymbol originalEnclosingMethod() {
1077 return originalOwner;
1078 }
1079 };
1080 } else {
1081 this.translatedSym = makePrivateSyntheticMethod(0, null, null, owner.enclClass());
1082 }
1083 ListBuffer<JCVariableDecl> params = new ListBuffer<>();
1084 ListBuffer<VarSymbol> parameterSymbols = new ListBuffer<>();
1085 LambdaCaptureScanner captureScanner = new LambdaCaptureScanner(tree);
1086 capturedVars = captureScanner.analyzeCaptures();
1087 for (VarSymbol captured : capturedVars) {
1088 VarSymbol trans = addSymbol(captured, LambdaSymbolKind.CAPTURED_VAR);
1089 params.append(make.VarDef(trans, null));
1090 parameterSymbols.add(trans);
1091 }
1092 for (JCVariableDecl param : tree.params) {
1093 VarSymbol trans = addSymbol(param.sym, LambdaSymbolKind.PARAM);
1094 params.append(make.VarDef(trans, null));
1095 parameterSymbols.add(trans);
1096 }
1097 syntheticParams = params.toList();
1098 completeLambdaMethodSymbol(owner, captureScanner.capturesThis);
1099 translatedSym.params = parameterSymbols.toList();
1100 }
1101
1102 void completeLambdaMethodSymbol(Symbol owner, boolean thisReferenced) {
1103 boolean inInterface = owner.enclClass().isInterface();
1104
1105 // Compute and set the lambda name
1106 Name name = isSerializable(tree)
1107 ? serializedLambdaName(owner)
1108 : lambdaName(owner);
1109
1110 //prepend synthetic args to translated lambda method signature
1111 Type type = types.createMethodTypeWithParameters(
1112 generatedLambdaSig(),
1113 TreeInfo.types(syntheticParams));
1114
1115 // If instance access isn't needed, make it static.
1116 // Interface instance methods must be default methods.
1117 // Lambda methods are private synthetic.
1118 // Inherit ACC_STRICT from the enclosing method, or, for clinit,
1119 // from the class.
1120 long flags = SYNTHETIC | LAMBDA_METHOD |
1121 owner.flags_field & STRICTFP |
1122 owner.owner.flags_field & STRICTFP |
1123 PRIVATE |
1124 (thisReferenced? (inInterface? DEFAULT : 0) : STATIC);
1125
1126 translatedSym.type = type;
1127 translatedSym.name = name;
1128 translatedSym.flags_field = flags;
1129 }
1130
1131 /**
1132 * For a serializable lambda, generate a disambiguating string
1133 * which maximizes stability across deserialization.
1134 *
1135 * @return String to differentiate synthetic lambda method names
1136 */
1137 private String serializedLambdaDisambiguation(Symbol owner) {
1138 StringBuilder buf = new StringBuilder();
1139 // Append the enclosing method signature to differentiate
1140 // overloaded enclosing methods. For lambdas enclosed in
1141 // lambdas, the generated lambda method will not have type yet,
1142 // but the enclosing method's name will have been generated
1143 // with this same method, so it will be unique and never be
1144 // overloaded.
1145 Assert.check(
1146 owner.type != null ||
1147 lambdaContext != null);
1148 if (owner.type != null) {
1149 buf.append(typeSig(owner.type, true));
1150 buf.append(":");
1151 }
1152
1153 // Add target type info
1154 buf.append(types.findDescriptorSymbol(tree.type.tsym).owner.flatName());
1155 buf.append(" ");
1156
1157 // Add variable assigned to
1158 if (pendingVar != null) {
1159 buf.append(pendingVar.flatName());
1160 buf.append("=");
1161 }
1162 //add captured locals info: type, name, order
1163 for (Symbol fv : capturedVars) {
1164 if (fv != owner) {
1165 buf.append(typeSig(fv.type, true));
1166 buf.append(" ");
1167 buf.append(fv.flatName());
1168 buf.append(",");
1169 }
1170 }
1171
1172 return buf.toString();
1173 }
1174
1175 /**
1176 * For a non-serializable lambda, generate a simple method.
1177 *
1178 * @return Name to use for the synthetic lambda method name
1179 */
1180 private Name lambdaName(Symbol owner) {
1181 StringBuilder buf = new StringBuilder();
1182 buf.append(names.lambda);
1183 buf.append(syntheticMethodNameComponent(owner));
1184 buf.append("$");
1185 buf.append(kInfo.syntheticNameIndex(buf, 0));
1186 return names.fromString(buf.toString());
1187 }
1188
1189 /**
1190 * @return Method name in a form that can be folded into a
1191 * component of a synthetic method name
1192 */
1193 String syntheticMethodNameComponent(Symbol owner) {
1194 long ownerFlags = owner.flags();
1195 if ((ownerFlags & BLOCK) != 0) {
1196 return (ownerFlags & STATIC) != 0 ?
1197 "static" : "new";
1198 } else if (owner.isConstructor()) {
1199 return "new";
1200 } else {
1201 return owner.name.toString();
1202 }
1203 }
1204
1205 /**
1206 * For a serializable lambda, generate a method name which maximizes
1207 * name stability across deserialization.
1208 *
1209 * @return Name to use for the synthetic lambda method name
1210 */
1211 private Name serializedLambdaName(Symbol owner) {
1212 StringBuilder buf = new StringBuilder();
1213 buf.append(names.lambda);
1214 // Append the name of the method enclosing the lambda.
1215 buf.append(syntheticMethodNameComponent(owner));
1216 buf.append('$');
1217 // Append a hash of the disambiguating string : enclosing method
1218 // signature, etc.
1219 String disam = serializedLambdaDisambiguation(owner);
1220 buf.append(Integer.toHexString(disam.hashCode()));
1221 buf.append('$');
1222 // The above appended name components may not be unique, append
1223 // a count based on the above name components.
1224 buf.append(kInfo.syntheticNameIndex(buf, 1));
1225 String result = buf.toString();
1226 //System.err.printf("serializedLambdaName: %s -- %s\n", result, disam);
1227 return names.fromString(result);
1228 }
1229
1230 /**
1231 * Translate a symbol of a given kind into something suitable for the
1232 * synthetic lambda body
1233 */
1234 VarSymbol translate(final VarSymbol sym, LambdaSymbolKind skind) {
1235 VarSymbol ret;
1236 boolean propagateAnnos = true;
1237 switch (skind) {
1238 case CAPTURED_VAR:
1239 Name name = (sym.flags() & LOCAL_CAPTURE_FIELD) != 0 ?
1240 sym.baseSymbol().name : sym.name;
1241 ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym);
1242 propagateAnnos = false;
1243 break;
1244 case LOCAL_VAR:
1245 ret = new VarSymbol(sym.flags(), sym.name, sym.type, translatedSym);
1246 ret.pos = sym.pos;
1247 // If sym.data == ElementKind.EXCEPTION_PARAMETER,
1248 // set ret.data = ElementKind.EXCEPTION_PARAMETER too.
1249 // Because method com.sun.tools.javac.jvm.Code.fillExceptionParameterPositions and
1250 // com.sun.tools.javac.jvm.Code.fillLocalVarPosition would use it.
1251 // See JDK-8257740 for more information.
1252 if (sym.isExceptionParameter()) {
1253 ret.setData(ElementKind.EXCEPTION_PARAMETER);
1254 }
1255 break;
1256 case PARAM:
1257 Assert.check((sym.flags() & PARAMETER) != 0);
1258 ret = new VarSymbol(sym.flags(), sym.name, types.erasure(sym.type), translatedSym);
1259 ret.pos = sym.pos;
1260 break;
1261 default:
1262 Assert.error(skind.name());
1263 throw new AssertionError();
1264 }
1265 if (ret != sym && propagateAnnos) {
1266 ret.setDeclarationAttributes(sym.getRawAttributes());
1267 ret.setTypeAttributes(sym.getRawTypeAttributes());
1268 }
1269 return ret;
1270 }
1271
1272 VarSymbol addLocal(VarSymbol sym) {
1273 return addSymbol(sym, LambdaSymbolKind.LOCAL_VAR);
1274 }
1275
1276 private VarSymbol addSymbol(VarSymbol sym, LambdaSymbolKind skind) {
1277 return lambdaProxies.computeIfAbsent(sym, s -> translate(s, skind));
1278 }
1279
1280 JCTree translate(JCIdent lambdaIdent) {
1281 Symbol tSym = lambdaProxies.get(lambdaIdent.sym);
1282 return tSym != null ?
1283 make.Ident(tSym).setType(lambdaIdent.type) :
1284 null;
1285 }
1286
1287 Type generatedLambdaSig() {
1288 return types.erasure(tree.getDescriptorType(types));
1289 }
1290
1291 /**
1292 * Compute the set of local variables captured by this lambda expression.
1293 * Also determines whether this lambda expression captures the enclosing 'this'.
1294 */
1295 class LambdaCaptureScanner extends CaptureScanner {
1296 boolean capturesThis;
1297 Set<ClassSymbol> seenClasses = new HashSet<>();
1298
1299 LambdaCaptureScanner(JCLambda ownerTree) {
1300 super(ownerTree);
1301 }
1302
1303 @Override
1304 public void visitClassDef(JCClassDecl tree) {
1305 seenClasses.add(tree.sym);
1306 super.visitClassDef(tree);
1307 }
1308
1309 @Override
1310 public void visitIdent(JCIdent tree) {
1311 if (!tree.sym.isStatic() &&
1312 tree.sym.owner.kind == TYP &&
1313 (tree.sym.kind == VAR || tree.sym.kind == MTH) &&
1314 !seenClasses.contains(tree.sym.owner)) {
1315 if ((tree.sym.flags() & LOCAL_CAPTURE_FIELD) != 0) {
1316 // a local, captured by Lower - re-capture!
1317 addFreeVar((VarSymbol) tree.sym);
1318 } else if (isEarlyInstanceFieldInit() &&
1319 (tree.sym.flags() & OUTER_THIS_FIELD) != 0) {
1320 // If we're in early strict instance initializer we can't assume this$0 is
1321 // accessible. So we should make the lambda method static, and deal with
1322 // this$0 as if it were a regular capture. This works because language rules
1323 // prevent direct access to this/super, so a static lambda method should
1324 // always be ok as a translation target in a ctor prologue.
1325 addFreeVar((VarSymbol) tree.sym);
1326 } else {
1327 // a reference to an enclosing field or method, we need to capture 'this'
1328 capturesThis = true;
1329 }
1330 } else {
1331 // might be a local capture
1332 super.visitIdent(tree);
1333 }
1334 }
1335
1336 @Override
1337 public void visitSelect(JCFieldAccess tree) {
1338 if (tree.sym.kind == VAR &&
1339 (tree.sym.name == names._this ||
1340 tree.sym.name == names._super) &&
1341 !seenClasses.contains(tree.sym.type.tsym)) {
1342 capturesThis = true;
1343 }
1344 super.visitSelect(tree);
1345 }
1346
1347 @Override
1348 public void visitAnnotation(JCAnnotation tree) {
1349 // do nothing (annotation values look like captured instance fields)
1350 }
1351
1352 private boolean isEarlyInstanceFieldInit() {
1353 return pendingVar != null &&
1354 pendingVar.isStrictInstance();
1355 }
1356 }
1357
1358 /*
1359 * These keys provide mappings for various translated lambda symbols
1360 * and the prevailing order must be maintained.
1361 */
1362 enum LambdaSymbolKind {
1363 PARAM, // original to translated lambda parameters
1364 LOCAL_VAR, // original to translated lambda locals
1365 CAPTURED_VAR; // variables in enclosing scope to translated synthetic parameters
1366 }
1367 }
1368
1369 /**
1370 * Deserialization statements for a given lambda implementation name, together
1371 * with the (future) enclosing deserialization method.
1372 */
1373 record DeserializationCase(MethodSymbol deserializationMethod,
1374 VarSymbol deserParamSym,
1375 ListBuffer<JCStatement> stmts) {}
1376
1377 /**
1378 * ****************************************************************
1379 * Signature Generation
1380 * ****************************************************************
1381 */
1382
1383 private String typeSig(Type type) {
1384 return typeSig(type, false);
1385 }
1386
1387 private String typeSig(Type type, boolean allowIllegalSignature) {
1388 try {
1389 L2MSignatureGenerator sg = new L2MSignatureGenerator(allowIllegalSignature);
1390 sg.assembleSig(type);
1391 return sg.toString();
1392 } catch (InvalidSignatureException ex) {
1393 Symbol c = attrEnv.enclClass.sym;
1394 log.error(Errors.CannotGenerateClass(c, Fragments.IllegalSignature(c, ex.type())));
1395 return "<ERRONEOUS>";
1396 }
1397 }
1398
1399 private String classSig(Type type) {
1400 try {
1401 L2MSignatureGenerator sg = new L2MSignatureGenerator(false);
1402 sg.assembleClassSig(type);
1403 return sg.toString();
1404 } catch (InvalidSignatureException ex) {
1405 Symbol c = attrEnv.enclClass.sym;
1406 log.error(Errors.CannotGenerateClass(c, Fragments.IllegalSignature(c, ex.type())));
1407 return "<ERRONEOUS>";
1408 }
1409 }
1410
1411 /**
1412 * Signature Generation
1413 */
1414 private class L2MSignatureGenerator extends Types.SignatureGenerator {
1415
1416 /**
1417 * An output buffer for type signatures.
1418 */
1419 StringBuilder sb = new StringBuilder();
1420
1421 /**
1422 * Are signatures incompatible with JVM spec allowed?
1423 * Used by {@link LambdaTranslationContext#serializedLambdaDisambiguation(Symbol)}}.
1424 */
1425 boolean allowIllegalSignatures;
1426
1427 L2MSignatureGenerator(boolean allowIllegalSignatures) {
1428 types.super();
1429 this.allowIllegalSignatures = allowIllegalSignatures;
1430 }
1431
1432 @Override
1433 protected void reportIllegalSignature(Type t) {
1434 if (!allowIllegalSignatures) {
1435 super.reportIllegalSignature(t);
1436 }
1437 }
1438
1439 @Override
1440 protected void append(char ch) {
1441 sb.append(ch);
1442 }
1443
1444 @Override
1445 protected void append(byte[] ba) {
1446 Name name;
1447 try {
1448 name = names.fromUtf(ba);
1449 } catch (InvalidUtfException e) {
1450 throw new AssertionError(e);
1451 }
1452 sb.append(name.toString());
1453 }
1454
1455 @Override
1456 protected void append(Name name) {
1457 sb.append(name.toString());
1458 }
1459
1460 @Override
1461 public String toString() {
1462 return sb.toString();
1463 }
1464 }
1465 }