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