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 Name lambdaName = samSym.name;
981 if (tree.codeReflectionInfo != null) {
982 lambdaName = lambdaName
983 .append(names.fromString("="))
984 .append(tree.codeReflectionInfo.codeModel().name);
985 }
986 Type lambdaMetafactory = tree.codeReflectionInfo != null ?
987 tree.codeReflectionInfo.reflectableLambdaMetafactory() : syms.lambdaMetafactory;
988 return makeIndyCall(tree, lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args, lambdaName);
989 }
990
991 /**
992 * Generate an indy method call with given name, type and static bootstrap
993 * arguments types
994 */
995 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
996 List<LoadableConstant> staticArgs, MethodType indyType, List<JCExpression> indyArgs,
997 Name methName) {
998 int prevPos = make.pos;
999 try {
1000 make.at(pos);
1001 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
1002 syms.stringType,
1003 syms.methodTypeType).appendList(staticArgs.map(types::constantType));
1004
1005 MethodSymbol bsm = rs.resolveInternalMethod(pos, attrEnv, site,
1006 bsmName, bsm_staticArgs, List.nil());
1007
1008 DynamicMethodSymbol dynSym =
1009 new DynamicMethodSymbol(methName,
1010 syms.noSymbol,
1011 bsm.asHandle(),
1012 indyType,
1013 staticArgs.toArray(new LoadableConstant[staticArgs.length()]));
1014 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName);
1015 DynamicMethodSymbol existing = kInfo.dynMethSyms.putIfAbsent(
1016 dynSym.poolKey(types), dynSym);
1017 qualifier.sym = existing != null ? existing : dynSym;
1018 qualifier.type = indyType.getReturnType();
1019
1020 JCMethodInvocation proxyCall = make.Apply(List.nil(), qualifier, indyArgs);
1021 proxyCall.type = indyType.getReturnType();
1022 return proxyCall;
1023 } finally {
1024 make.at(prevPos);
1025 }
1026 }
1027
1028 List<Symbol> bridges(JCFunctionalExpression tree) {
1029 ClassSymbol csym =
1030 types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.target, ABSTRACT | INTERFACE);
1031 return types.functionalInterfaceBridges(csym);
1032 }
1033
1034 /** does this functional expression require serialization support? */
1035 boolean isSerializable(JCFunctionalExpression tree) {
1036 if (forceSerializable) {
1037 return true;
1038 }
1039 return types.asSuper(tree.target, syms.serializableType.tsym) != null;
1040 }
1041
1042 void dumpStats(JCFunctionalExpression tree, boolean needsAltMetafactory, Symbol sym) {
1043 if (dumpLambdaToMethodStats) {
1044 if (tree instanceof JCLambda lambda) {
1045 log.note(tree, diags.noteKey(lambda.wasMethodReference ? "mref.stat.1" : "lambda.stat",
1046 needsAltMetafactory, sym));
1047 } else if (tree instanceof JCMemberReference) {
1048 log.note(tree, Notes.MrefStat(needsAltMetafactory, null));
1049 }
1050 }
1051 }
1052
1053 /**
1054 * This class retains all the useful information about a lambda expression,
1055 * and acts as a translation map that is used by the main translation routines
1056 * in order to adjust references to captured locals/members, etc.
1057 */
1058 class LambdaTranslationContext {
1059
1060 /** the underlying (untranslated) tree */
1061 final JCFunctionalExpression tree;
1062
1063 /** a translation map from source symbols to translated symbols */
1064 final Map<VarSymbol, VarSymbol> lambdaProxies = new HashMap<>();
1065
1066 /** the list of symbols captured by this lambda expression */
1067 final List<VarSymbol> capturedVars;
1068
1069 /** the synthetic symbol for the method hoisting the translated lambda */
1070 final MethodSymbol translatedSym;
1071
1072 /** the list of parameter declarations of the translated lambda method */
1073 final List<JCVariableDecl> syntheticParams;
1074
1075 LambdaTranslationContext(JCLambda tree) {
1076 this.tree = tree;
1077 // This symbol will be filled-in in complete
1078 Symbol owner = tree.owner;
1079 if (owner.kind == MTH) {
1080 final MethodSymbol originalOwner = (MethodSymbol)owner.clone(owner.owner);
1081 this.translatedSym = new MethodSymbol(0, null, null, owner.enclClass()) {
1082 @Override
1083 public MethodSymbol originalEnclosingMethod() {
1084 return originalOwner;
1085 }
1086 };
1087 } else {
1088 this.translatedSym = makePrivateSyntheticMethod(0, null, null, owner.enclClass());
1089 }
1090 ListBuffer<JCVariableDecl> params = new ListBuffer<>();
1091 ListBuffer<VarSymbol> parameterSymbols = new ListBuffer<>();
1092 LambdaCaptureScanner captureScanner = new LambdaCaptureScanner(tree);
1093 capturedVars = captureScanner.analyzeCaptures();
1094 for (VarSymbol captured : capturedVars) {
1095 VarSymbol trans = addSymbol(captured, LambdaSymbolKind.CAPTURED_VAR);
1096 params.append(make.VarDef(trans, null));
1097 parameterSymbols.add(trans);
1098 }
1099 for (JCVariableDecl param : tree.params) {
1100 VarSymbol trans = addSymbol(param.sym, LambdaSymbolKind.PARAM);
1101 params.append(make.VarDef(trans, null));
1102 parameterSymbols.add(trans);
1103 }
1104 syntheticParams = params.toList();
1105 completeLambdaMethodSymbol(owner, captureScanner.capturesThis);
1106 translatedSym.params = parameterSymbols.toList();
1107 }
1108
1109 void completeLambdaMethodSymbol(Symbol owner, boolean thisReferenced) {
1110 boolean inInterface = owner.enclClass().isInterface();
1111
1112 // Compute and set the lambda name
1113 Name name = isSerializable(tree)
1114 ? serializedLambdaName(owner)
1115 : lambdaName(owner);
1116
1117 //prepend synthetic args to translated lambda method signature
1118 Type type = types.createMethodTypeWithParameters(
1119 generatedLambdaSig(),
1120 TreeInfo.types(syntheticParams));
1121
1122 // If instance access isn't needed, make it static.
1123 // Interface instance methods must be default methods.
1124 // Lambda methods are private synthetic.
1125 // Inherit ACC_STRICT from the enclosing method, or, for clinit,
1126 // from the class.
1127 long flags = SYNTHETIC | LAMBDA_METHOD |
1128 owner.flags_field & STRICTFP |
1129 owner.owner.flags_field & STRICTFP |
1130 PRIVATE |
1131 (thisReferenced? (inInterface? DEFAULT : 0) : STATIC);
1132
1133 translatedSym.type = type;
1134 translatedSym.name = name;
1135 translatedSym.flags_field = flags;
1136 }
1137
1138 /**
1139 * For a serializable lambda, generate a disambiguating string
1140 * which maximizes stability across deserialization.
1141 *
1142 * @return String to differentiate synthetic lambda method names
1143 */
1144 private String serializedLambdaDisambiguation(Symbol owner) {
1145 StringBuilder buf = new StringBuilder();
1146 // Append the enclosing method signature to differentiate
1147 // overloaded enclosing methods. For lambdas enclosed in
1148 // lambdas, the generated lambda method will not have type yet,
1149 // but the enclosing method's name will have been generated
1150 // with this same method, so it will be unique and never be
1151 // overloaded.
1152 Assert.check(
1153 owner.type != null ||
1154 lambdaContext != null);
1155 if (owner.type != null) {
1156 buf.append(typeSig(owner.type, true));
1157 buf.append(":");
1158 }
1159
1160 // Add target type info
1161 buf.append(types.findDescriptorSymbol(tree.type.tsym).owner.flatName());
1162 buf.append(" ");
1163
1164 // Add variable assigned to
1165 if (pendingVar != null) {
1166 buf.append(pendingVar.flatName());
1167 buf.append("=");
1168 }
1169 //add captured locals info: type, name, order
1170 for (Symbol fv : capturedVars) {
1171 if (fv != owner) {
1172 buf.append(typeSig(fv.type, true));
1173 buf.append(" ");
1174 buf.append(fv.flatName());
1175 buf.append(",");
1176 }
1177 }
1178
1179 return buf.toString();
1180 }
1181
1182 /**
1183 * For a non-serializable lambda, generate a simple method.
1184 *
1185 * @return Name to use for the synthetic lambda method name
1186 */
1187 private Name lambdaName(Symbol owner) {
1188 StringBuilder buf = new StringBuilder();
1189 buf.append(names.lambda);
1190 buf.append(syntheticMethodNameComponent(owner));
1191 buf.append("$");
1192 buf.append(kInfo.syntheticNameIndex(buf, 0));
1193 return names.fromString(buf.toString());
1194 }
1195
1196 /**
1197 * @return Method name in a form that can be folded into a
1198 * component of a synthetic method name
1199 */
1200 String syntheticMethodNameComponent(Symbol owner) {
1201 long ownerFlags = owner.flags();
1202 if ((ownerFlags & BLOCK) != 0) {
1203 return (ownerFlags & STATIC) != 0 ?
1204 "static" : "new";
1205 } else if (owner.isConstructor()) {
1206 return "new";
1207 } else {
1208 return owner.name.toString();
1209 }
1210 }
1211
1212 /**
1213 * For a serializable lambda, generate a method name which maximizes
1214 * name stability across deserialization.
1215 *
1216 * @return Name to use for the synthetic lambda method name
1217 */
1218 private Name serializedLambdaName(Symbol owner) {
1219 StringBuilder buf = new StringBuilder();
1220 buf.append(names.lambda);
1221 // Append the name of the method enclosing the lambda.
1222 buf.append(syntheticMethodNameComponent(owner));
1223 buf.append('$');
1224 // Append a hash of the disambiguating string : enclosing method
1225 // signature, etc.
1226 String disam = serializedLambdaDisambiguation(owner);
1227 buf.append(Integer.toHexString(disam.hashCode()));
1228 buf.append('$');
1229 // The above appended name components may not be unique, append
1230 // a count based on the above name components.
1231 buf.append(kInfo.syntheticNameIndex(buf, 1));
1232 String result = buf.toString();
1233 //System.err.printf("serializedLambdaName: %s -- %s\n", result, disam);
1234 return names.fromString(result);
1235 }
1236
1237 /**
1238 * Translate a symbol of a given kind into something suitable for the
1239 * synthetic lambda body
1240 */
1241 VarSymbol translate(final VarSymbol sym, LambdaSymbolKind skind) {
1242 VarSymbol ret;
1243 boolean propagateAnnos = true;
1244 switch (skind) {
1245 case CAPTURED_VAR:
1246 Name name = (sym.flags() & LOCAL_CAPTURE_FIELD) != 0 ?
1247 sym.baseSymbol().name : sym.name;
1248 ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym);
1249 propagateAnnos = false;
1250 break;
1251 case LOCAL_VAR:
1252 ret = new VarSymbol(sym.flags(), sym.name, sym.type, translatedSym);
1253 ret.pos = sym.pos;
1254 // If sym.data == ElementKind.EXCEPTION_PARAMETER,
1255 // set ret.data = ElementKind.EXCEPTION_PARAMETER too.
1256 // Because method com.sun.tools.javac.jvm.Code.fillExceptionParameterPositions and
1257 // com.sun.tools.javac.jvm.Code.fillLocalVarPosition would use it.
1258 // See JDK-8257740 for more information.
1259 if (sym.isExceptionParameter()) {
1260 ret.setData(ElementKind.EXCEPTION_PARAMETER);
1261 }
1262 break;
1263 case PARAM:
1264 Assert.check((sym.flags() & PARAMETER) != 0);
1265 ret = new VarSymbol(sym.flags(), sym.name, types.erasure(sym.type), translatedSym);
1266 ret.pos = sym.pos;
1267 break;
1268 default:
1269 Assert.error(skind.name());
1270 throw new AssertionError();
1271 }
1272 if (ret != sym && propagateAnnos) {
1273 ret.setDeclarationAttributes(sym.getRawAttributes());
1274 ret.setTypeAttributes(sym.getRawTypeAttributes());
1275 }
1276 return ret;
1277 }
1278
1279 VarSymbol addLocal(VarSymbol sym) {
1280 return addSymbol(sym, LambdaSymbolKind.LOCAL_VAR);
1281 }
1282
1283 private VarSymbol addSymbol(VarSymbol sym, LambdaSymbolKind skind) {
1284 return lambdaProxies.computeIfAbsent(sym, s -> translate(s, skind));
1285 }
1286
1287 JCTree translate(JCIdent lambdaIdent) {
1288 Symbol tSym = lambdaProxies.get(lambdaIdent.sym);
1289 return tSym != null ?
1290 make.Ident(tSym).setType(lambdaIdent.type) :
1291 null;
1292 }
1293
1294 Type generatedLambdaSig() {
1295 return types.erasure(tree.getDescriptorType(types));
1296 }
1297
1298 /**
1299 * Compute the set of local variables captured by this lambda expression.
1300 * Also determines whether this lambda expression captures the enclosing 'this'.
1301 */
1302 class LambdaCaptureScanner extends CaptureScanner {
1303 boolean capturesThis;
1304 Set<ClassSymbol> seenClasses = new HashSet<>();
1305
1306 LambdaCaptureScanner(JCLambda ownerTree) {
1307 super(ownerTree);
1308 }
1309
1310 @Override
1311 public void visitClassDef(JCClassDecl tree) {
1312 seenClasses.add(tree.sym);
1313 super.visitClassDef(tree);
1314 }
1315
1316 @Override
1317 public void visitIdent(JCIdent tree) {
1318 if (!tree.sym.isStatic() &&
1319 tree.sym.owner.kind == TYP &&
1320 (tree.sym.kind == VAR || tree.sym.kind == MTH) &&
1321 !seenClasses.contains(tree.sym.owner)) {
1322 if ((tree.sym.flags() & LOCAL_CAPTURE_FIELD) != 0) {
1323 // a local, captured by Lower - re-capture!
1324 addFreeVar((VarSymbol) tree.sym);
1325 } else {
1326 // a reference to an enclosing field or method, we need to capture 'this'
1327 capturesThis = true;
1328 }
1329 } else {
1330 // might be a local capture
1331 super.visitIdent(tree);
1332 }
1333 }
1334
1335 @Override
1336 public void visitSelect(JCFieldAccess tree) {
1337 if (tree.sym.kind == VAR &&
1338 (tree.sym.name == names._this ||
1339 tree.sym.name == names._super) &&
1340 !seenClasses.contains(tree.sym.type.tsym)) {
1341 capturesThis = true;
1342 }
1343 super.visitSelect(tree);
1344 }
1345
1346 @Override
1347 public void visitAnnotation(JCAnnotation tree) {
1348 // do nothing (annotation values look like captured instance fields)
1349 }
1350 }
1351
1352 /*
1353 * These keys provide mappings for various translated lambda symbols
1354 * and the prevailing order must be maintained.
1355 */
1356 enum LambdaSymbolKind {
1357 PARAM, // original to translated lambda parameters
1358 LOCAL_VAR, // original to translated lambda locals
1359 CAPTURED_VAR; // variables in enclosing scope to translated synthetic parameters
1360 }
1361 }
1362
1363 /**
1364 * Deserialization statements for a given lambda implementation name, together
1365 * with the (future) enclosing deserialization method.
1366 */
1367 record DeserializationCase(MethodSymbol deserializationMethod,
1368 VarSymbol deserParamSym,
1369 ListBuffer<JCStatement> stmts) {}
1370
1371 /**
1372 * ****************************************************************
1373 * Signature Generation
1374 * ****************************************************************
1375 */
1376
1377 private String typeSig(Type type) {
1378 return typeSig(type, false);
1379 }
1380
1381 private String typeSig(Type type, boolean allowIllegalSignature) {
1382 try {
1383 L2MSignatureGenerator sg = new L2MSignatureGenerator(allowIllegalSignature);
1384 sg.assembleSig(type);
1385 return sg.toString();
1386 } catch (InvalidSignatureException ex) {
1387 Symbol c = attrEnv.enclClass.sym;
1388 log.error(Errors.CannotGenerateClass(c, Fragments.IllegalSignature(c, ex.type())));
1389 return "<ERRONEOUS>";
1390 }
1391 }
1392
1393 private String classSig(Type type) {
1394 try {
1395 L2MSignatureGenerator sg = new L2MSignatureGenerator(false);
1396 sg.assembleClassSig(type);
1397 return sg.toString();
1398 } catch (InvalidSignatureException ex) {
1399 Symbol c = attrEnv.enclClass.sym;
1400 log.error(Errors.CannotGenerateClass(c, Fragments.IllegalSignature(c, ex.type())));
1401 return "<ERRONEOUS>";
1402 }
1403 }
1404
1405 /**
1406 * Signature Generation
1407 */
1408 private class L2MSignatureGenerator extends Types.SignatureGenerator {
1409
1410 /**
1411 * An output buffer for type signatures.
1412 */
1413 StringBuilder sb = new StringBuilder();
1414
1415 /**
1416 * Are signatures incompatible with JVM spec allowed?
1417 * Used by {@link LambdaTranslationContext#serializedLambdaDisambiguation(Symbol)}}.
1418 */
1419 boolean allowIllegalSignatures;
1420
1421 L2MSignatureGenerator(boolean allowIllegalSignatures) {
1422 types.super();
1423 this.allowIllegalSignatures = allowIllegalSignatures;
1424 }
1425
1426 @Override
1427 protected void reportIllegalSignature(Type t) {
1428 if (!allowIllegalSignatures) {
1429 super.reportIllegalSignature(t);
1430 }
1431 }
1432
1433 @Override
1434 protected void append(char ch) {
1435 sb.append(ch);
1436 }
1437
1438 @Override
1439 protected void append(byte[] ba) {
1440 Name name;
1441 try {
1442 name = names.fromUtf(ba);
1443 } catch (InvalidUtfException e) {
1444 throw new AssertionError(e);
1445 }
1446 sb.append(name.toString());
1447 }
1448
1449 @Override
1450 protected void append(Name name) {
1451 sb.append(name.toString());
1452 }
1453
1454 @Override
1455 public String toString() {
1456 return sb.toString();
1457 }
1458 }
1459 }