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