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