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