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