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 }