1 /* 2 * Copyright (c) 2010, 2022, 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.Symbol.MethodHandleSymbol; 29 import com.sun.tools.javac.code.Types.SignatureGenerator.InvalidSignatureException; 30 import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant; 31 import com.sun.tools.javac.resources.CompilerProperties.Errors; 32 import com.sun.tools.javac.resources.CompilerProperties.Fragments; 33 import com.sun.tools.javac.tree.*; 34 import com.sun.tools.javac.tree.JCTree.*; 35 import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind; 36 import com.sun.tools.javac.tree.TreeMaker; 37 import com.sun.tools.javac.tree.TreeTranslator; 38 import com.sun.tools.javac.code.Attribute; 39 import com.sun.tools.javac.code.Symbol; 40 import com.sun.tools.javac.code.Symbol.ClassSymbol; 41 import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol; 42 import com.sun.tools.javac.code.Symbol.MethodSymbol; 43 import com.sun.tools.javac.code.Symbol.TypeSymbol; 44 import com.sun.tools.javac.code.Symbol.VarSymbol; 45 import com.sun.tools.javac.code.Symtab; 46 import com.sun.tools.javac.code.Type; 47 import com.sun.tools.javac.code.Type.MethodType; 48 import com.sun.tools.javac.code.Type.TypeVar; 49 import com.sun.tools.javac.code.Types; 50 import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzerPreprocessor.*; 51 import com.sun.tools.javac.comp.Lower.BasicFreeVarCollector; 52 import com.sun.tools.javac.resources.CompilerProperties.Notes; 53 import com.sun.tools.javac.jvm.*; 54 import com.sun.tools.javac.util.*; 55 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 56 import com.sun.source.tree.MemberReferenceTree.ReferenceMode; 57 58 import java.util.EnumMap; 59 import java.util.HashMap; 60 import java.util.HashSet; 61 import java.util.LinkedHashMap; 62 import java.util.Map; 63 import java.util.Optional; 64 import java.util.Set; 65 import java.util.function.Consumer; 66 import java.util.function.Supplier; 67 68 import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*; 69 import static com.sun.tools.javac.code.Flags.*; 70 import static com.sun.tools.javac.code.Kinds.Kind.*; 71 import static com.sun.tools.javac.code.TypeTag.*; 72 import static com.sun.tools.javac.tree.JCTree.Tag.*; 73 74 import javax.lang.model.element.ElementKind; 75 import javax.lang.model.type.TypeKind; 76 77 import com.sun.tools.javac.main.Option; 78 79 /** 80 * This pass desugars lambda expressions into static methods 81 * 82 * <p><b>This is NOT part of any supported API. 83 * If you write code that depends on this, you do so at your own risk. 84 * This code and its internal interfaces are subject to change or 85 * deletion without notice.</b> 86 */ 87 public class LambdaToMethod extends TreeTranslator { 88 89 private Attr attr; 90 private JCDiagnostic.Factory diags; 91 private Log log; 92 private Lower lower; 93 private Names names; 94 private Symtab syms; 95 private Resolve rs; 96 private Operators operators; 97 private TreeMaker make; 98 private Types types; 99 private TransTypes transTypes; 100 private Env<AttrContext> attrEnv; 101 102 /** the analyzer scanner */ 103 private LambdaAnalyzerPreprocessor analyzer; 104 105 /** map from lambda trees to translation contexts */ 106 private Map<JCTree, TranslationContext<?>> contextMap; 107 108 /** current translation context (visitor argument) */ 109 private TranslationContext<?> context; 110 111 /** info about the current class being processed */ 112 private KlassInfo kInfo; 113 114 /** dump statistics about lambda code generation */ 115 private final boolean dumpLambdaToMethodStats; 116 117 /** force serializable representation, for stress testing **/ 118 private final boolean forceSerializable; 119 120 /** true if line or local variable debug info has been requested */ 121 private final boolean debugLinesOrVars; 122 123 /** dump statistics about lambda method deduplication */ 124 private final boolean verboseDeduplication; 125 126 /** deduplicate lambda implementation methods */ 127 private final boolean deduplicateLambdas; 128 129 /** lambda proxy is a dynamic nestmate */ 130 private final boolean nestmateLambdas; 131 132 /** Flag for alternate metafactories indicating the lambda object is intended to be serializable */ 133 public static final int FLAG_SERIALIZABLE = 1 << 0; 134 135 /** Flag for alternate metafactories indicating the lambda object has multiple targets */ 136 public static final int FLAG_MARKERS = 1 << 1; 137 138 /** Flag for alternate metafactories indicating the lambda object requires multiple bridges */ 139 public static final int FLAG_BRIDGES = 1 << 2; 140 141 // <editor-fold defaultstate="collapsed" desc="Instantiating"> 142 protected static final Context.Key<LambdaToMethod> unlambdaKey = new Context.Key<>(); 143 144 public static LambdaToMethod instance(Context context) { 145 LambdaToMethod instance = context.get(unlambdaKey); 146 if (instance == null) { 147 instance = new LambdaToMethod(context); 148 } 149 return instance; 150 } 151 private LambdaToMethod(Context context) { 152 context.put(unlambdaKey, this); 153 diags = JCDiagnostic.Factory.instance(context); 154 log = Log.instance(context); 155 lower = Lower.instance(context); 156 names = Names.instance(context); 157 syms = Symtab.instance(context); 158 rs = Resolve.instance(context); 159 operators = Operators.instance(context); 160 make = TreeMaker.instance(context); 161 types = Types.instance(context); 162 transTypes = TransTypes.instance(context); 163 analyzer = new LambdaAnalyzerPreprocessor(); 164 Options options = Options.instance(context); 165 dumpLambdaToMethodStats = options.isSet("debug.dumpLambdaToMethodStats"); 166 attr = Attr.instance(context); 167 forceSerializable = options.isSet("forceSerializable"); 168 boolean lineDebugInfo = 169 options.isUnset(Option.G_CUSTOM) || 170 options.isSet(Option.G_CUSTOM, "lines"); 171 boolean varDebugInfo = 172 options.isUnset(Option.G_CUSTOM) 173 ? options.isSet(Option.G) 174 : options.isSet(Option.G_CUSTOM, "vars"); 175 debugLinesOrVars = lineDebugInfo || varDebugInfo; 176 verboseDeduplication = options.isSet("debug.dumpLambdaToMethodDeduplication"); 177 deduplicateLambdas = options.getBoolean("deduplicateLambdas", true); 178 nestmateLambdas = Target.instance(context).runtimeUseNestAccess(); 179 } 180 // </editor-fold> 181 182 class DedupedLambda { 183 private final MethodSymbol symbol; 184 private final JCTree tree; 185 186 private int hashCode; 187 188 DedupedLambda(MethodSymbol symbol, JCTree tree) { 189 this.symbol = symbol; 190 this.tree = tree; 191 } 192 193 194 @Override 195 public int hashCode() { 196 int hashCode = this.hashCode; 197 if (hashCode == 0) { 198 this.hashCode = hashCode = TreeHasher.hash(tree, symbol.params()); 199 } 200 return hashCode; 201 } 202 203 @Override 204 public boolean equals(Object o) { 205 return (o instanceof DedupedLambda dedupedLambda) 206 && types.isSameType(symbol.asType(), dedupedLambda.symbol.asType()) 207 && new TreeDiffer(symbol.params(), dedupedLambda.symbol.params()).scan(tree, dedupedLambda.tree); 208 } 209 } 210 211 private class KlassInfo { 212 213 /** 214 * list of methods to append 215 */ 216 private ListBuffer<JCTree> appendedMethodList; 217 218 private Map<DedupedLambda, DedupedLambda> dedupedLambdas; 219 220 private Map<Object, DynamicMethodSymbol> dynMethSyms = new HashMap<>(); 221 222 /** 223 * list of deserialization cases 224 */ 225 private final Map<String, ListBuffer<JCStatement>> deserializeCases; 226 227 /** 228 * deserialize method symbol 229 */ 230 private final MethodSymbol deserMethodSym; 231 232 /** 233 * deserialize method parameter symbol 234 */ 235 private final VarSymbol deserParamSym; 236 237 private final JCClassDecl clazz; 238 239 private KlassInfo(JCClassDecl clazz) { 240 this.clazz = clazz; 241 appendedMethodList = new ListBuffer<>(); 242 dedupedLambdas = new HashMap<>(); 243 deserializeCases = new HashMap<>(); 244 MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType, 245 List.nil(), syms.methodClass); 246 deserMethodSym = makePrivateSyntheticMethod(STATIC, names.deserializeLambda, type, clazz.sym); 247 deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"), 248 syms.serializedLambdaType, deserMethodSym); 249 } 250 251 private void addMethod(JCTree decl) { 252 appendedMethodList = appendedMethodList.prepend(decl); 253 } 254 } 255 256 // <editor-fold defaultstate="collapsed" desc="translate methods"> 257 @Override 258 public <T extends JCTree> T translate(T tree) { 259 TranslationContext<?> newContext = contextMap.get(tree); 260 return translate(tree, newContext != null ? newContext : context); 261 } 262 263 <T extends JCTree> T translate(T tree, TranslationContext<?> newContext) { 264 TranslationContext<?> prevContext = context; 265 try { 266 context = newContext; 267 return super.translate(tree); 268 } 269 finally { 270 context = prevContext; 271 } 272 } 273 274 <T extends JCTree> List<T> translate(List<T> trees, TranslationContext<?> newContext) { 275 ListBuffer<T> buf = new ListBuffer<>(); 276 for (T tree : trees) { 277 buf.append(translate(tree, newContext)); 278 } 279 return buf.toList(); 280 } 281 282 public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) { 283 this.make = make; 284 this.attrEnv = env; 285 this.context = null; 286 this.contextMap = new HashMap<>(); 287 return translate(cdef); 288 } 289 // </editor-fold> 290 291 // <editor-fold defaultstate="collapsed" desc="visitor methods"> 292 /** 293 * Visit a class. 294 * Maintain the translatedMethodList across nested classes. 295 * Append the translatedMethodList to the class after it is translated. 296 * @param tree 297 */ 298 @Override 299 public void visitClassDef(JCClassDecl tree) { 300 if (tree.sym.owner.kind == PCK) { 301 //analyze class 302 tree = analyzer.analyzeAndPreprocessClass(tree); 303 } 304 KlassInfo prevKlassInfo = kInfo; 305 try { 306 kInfo = new KlassInfo(tree); 307 super.visitClassDef(tree); 308 if (!kInfo.deserializeCases.isEmpty()) { 309 int prevPos = make.pos; 310 try { 311 make.at(tree); 312 kInfo.addMethod(makeDeserializeMethod(tree.sym)); 313 } finally { 314 make.at(prevPos); 315 } 316 } 317 //add all translated instance methods here 318 List<JCTree> newMethods = kInfo.appendedMethodList.toList(); 319 tree.defs = tree.defs.appendList(newMethods); 320 for (JCTree lambda : newMethods) { 321 tree.sym.members().enter(((JCMethodDecl)lambda).sym); 322 } 323 result = tree; 324 } finally { 325 kInfo = prevKlassInfo; 326 } 327 } 328 329 /** 330 * Translate a lambda into a method to be inserted into the class. 331 * Then replace the lambda site with an invokedynamic call of to lambda 332 * meta-factory, which will use the lambda method. 333 * @param tree 334 */ 335 @Override 336 public void visitLambda(JCLambda tree) { 337 LambdaTranslationContext localContext = (LambdaTranslationContext)context; 338 MethodSymbol sym = localContext.translatedSym; 339 MethodType lambdaType = (MethodType) sym.type; 340 341 { /* Type annotation management: Based on where the lambda features, type annotations that 342 are interior to it, may at this point be attached to the enclosing method, or the first 343 constructor in the class, or in the enclosing class symbol or in the field whose 344 initializer is the lambda. In any event, gather up the annotations that belong to the 345 lambda and attach it to the implementation method. 346 */ 347 348 Symbol owner = localContext.owner; 349 apportionTypeAnnotations(tree, 350 owner::getRawTypeAttributes, 351 owner::setTypeAttributes, 352 sym::setTypeAttributes); 353 354 355 boolean init; 356 if ((init = (owner.name == names.init)) || owner.name == names.clinit) { 357 owner = owner.owner; 358 apportionTypeAnnotations(tree, 359 init ? owner::getInitTypeAttributes : owner::getClassInitTypeAttributes, 360 init ? owner::setInitTypeAttributes : owner::setClassInitTypeAttributes, 361 sym::appendUniqueTypeAttributes); 362 } 363 if (localContext.self != null && localContext.self.getKind() == ElementKind.FIELD) { 364 owner = localContext.self; 365 apportionTypeAnnotations(tree, 366 owner::getRawTypeAttributes, 367 owner::setTypeAttributes, 368 sym::appendUniqueTypeAttributes); 369 } 370 } 371 372 //create the method declaration hoisting the lambda body 373 JCMethodDecl lambdaDecl = make.MethodDef(make.Modifiers(sym.flags_field), 374 sym.name, 375 make.QualIdent(lambdaType.getReturnType().tsym), 376 List.nil(), 377 localContext.syntheticParams, 378 lambdaType.getThrownTypes() == null ? 379 List.nil() : 380 make.Types(lambdaType.getThrownTypes()), 381 null, 382 null); 383 lambdaDecl.sym = sym; 384 lambdaDecl.type = lambdaType; 385 386 //translate lambda body 387 //As the lambda body is translated, all references to lambda locals, 388 //captured variables, enclosing members are adjusted accordingly 389 //to refer to the static method parameters (rather than i.e. accessing 390 //captured members directly). 391 lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl)); 392 393 boolean dedupe = false; 394 if (deduplicateLambdas && !debugLinesOrVars && !localContext.isSerializable()) { 395 DedupedLambda dedupedLambda = new DedupedLambda(lambdaDecl.sym, lambdaDecl.body); 396 DedupedLambda existing = kInfo.dedupedLambdas.putIfAbsent(dedupedLambda, dedupedLambda); 397 if (existing != null) { 398 sym = existing.symbol; 399 dedupe = true; 400 if (verboseDeduplication) log.note(tree, Notes.VerboseL2mDeduplicate(sym)); 401 } 402 } 403 if (!dedupe) { 404 //Add the method to the list of methods to be added to this class. 405 kInfo.addMethod(lambdaDecl); 406 } 407 408 //now that we have generated a method for the lambda expression, 409 //we can translate the lambda into a method reference pointing to the newly 410 //created method. 411 // 412 //Note that we need to adjust the method handle so that it will match the 413 //signature of the SAM descriptor - this means that the method reference 414 //should be added the following synthetic arguments: 415 // 416 // * the "this" argument if it is an instance method 417 // * enclosing locals captured by the lambda expression 418 419 ListBuffer<JCExpression> syntheticInits = new ListBuffer<>(); 420 421 if (localContext.methodReferenceReceiver != null) { 422 syntheticInits.append(localContext.methodReferenceReceiver); 423 } else if (!sym.isStatic()) { 424 syntheticInits.append(makeThis( 425 sym.owner.enclClass().asType(), 426 localContext.owner.enclClass())); 427 } 428 429 //add captured locals 430 for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) { 431 if (fv != localContext.self) { 432 JCExpression captured_local = make.Ident(fv).setType(fv.type); 433 syntheticInits.append(captured_local); 434 } 435 } 436 // add captured outer this instances (used only when `this' capture itself is illegal) 437 for (Symbol fv : localContext.getSymbolMap(CAPTURED_OUTER_THIS).keySet()) { 438 JCExpression captured_local = make.QualThis(fv.type); 439 syntheticInits.append(captured_local); 440 } 441 442 //then, determine the arguments to the indy call 443 List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev); 444 445 //convert to an invokedynamic call 446 result = makeMetafactoryIndyCall(context, sym.asHandle(), indy_args); 447 } 448 449 // where 450 // Reassign type annotations from the source that should really belong to the lambda 451 private void apportionTypeAnnotations(JCLambda tree, 452 Supplier<List<Attribute.TypeCompound>> source, 453 Consumer<List<Attribute.TypeCompound>> owner, 454 Consumer<List<Attribute.TypeCompound>> lambda) { 455 456 ListBuffer<Attribute.TypeCompound> ownerTypeAnnos = new ListBuffer<>(); 457 ListBuffer<Attribute.TypeCompound> lambdaTypeAnnos = new ListBuffer<>(); 458 459 for (Attribute.TypeCompound tc : source.get()) { 460 if (tc.position.onLambda == tree) { 461 lambdaTypeAnnos.append(tc); 462 } else { 463 ownerTypeAnnos.append(tc); 464 } 465 } 466 if (lambdaTypeAnnos.nonEmpty()) { 467 owner.accept(ownerTypeAnnos.toList()); 468 lambda.accept(lambdaTypeAnnos.toList()); 469 } 470 } 471 472 private JCIdent makeThis(Type type, Symbol owner) { 473 VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC, 474 names._this, 475 type, 476 owner); 477 return make.Ident(_this); 478 } 479 480 /** 481 * Translate a method reference into an invokedynamic call to the 482 * meta-factory. 483 * @param tree 484 */ 485 @Override 486 public void visitReference(JCMemberReference tree) { 487 ReferenceTranslationContext localContext = (ReferenceTranslationContext)context; 488 489 //first determine the method symbol to be used to generate the sam instance 490 //this is either the method reference symbol, or the bridged reference symbol 491 MethodSymbol refSym = (MethodSymbol)tree.sym; 492 493 //the qualifying expression is treated as a special captured arg 494 JCExpression init; 495 switch(tree.kind) { 496 497 case IMPLICIT_INNER: /** Inner :: new */ 498 case SUPER: /** super :: instMethod */ 499 init = makeThis( 500 localContext.owner.enclClass().asType(), 501 localContext.owner.enclClass()); 502 break; 503 504 case BOUND: /** Expr :: instMethod */ 505 init = transTypes.coerce(attrEnv, tree.getQualifierExpression(), 506 types.erasure(tree.sym.owner.type)); 507 init = attr.makeNullCheck(init); 508 break; 509 510 case UNBOUND: /** Type :: instMethod */ 511 case STATIC: /** Type :: staticMethod */ 512 case TOPLEVEL: /** Top level :: new */ 513 case ARRAY_CTOR: /** ArrayType :: new */ 514 init = null; 515 break; 516 517 default: 518 throw new InternalError("Should not have an invalid kind"); 519 } 520 521 if (init != null) { 522 refSym = (MethodSymbol) types.binaryQualifier(refSym, init.type); 523 } 524 List<JCExpression> indy_args = init==null? List.nil() : translate(List.of(init), localContext.prev); 525 526 527 //build a sam instance using an indy call to the meta-factory 528 result = makeMetafactoryIndyCall(localContext, refSym.asHandle(), indy_args); 529 } 530 531 /** 532 * Translate identifiers within a lambda to the mapped identifier 533 * @param tree 534 */ 535 @Override 536 public void visitIdent(JCIdent tree) { 537 if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) { 538 super.visitIdent(tree); 539 } else { 540 int prevPos = make.pos; 541 try { 542 make.at(tree); 543 544 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context; 545 JCTree ltree = lambdaContext.translate(tree); 546 if (ltree != null) { 547 result = ltree; 548 } else { 549 //access to untranslated symbols (i.e. compile-time constants, 550 //members defined inside the lambda body, etc.) ) 551 super.visitIdent(tree); 552 } 553 } finally { 554 make.at(prevPos); 555 } 556 } 557 } 558 559 /** 560 * Translate qualified `this' references within a lambda to the mapped identifier 561 * @param tree 562 */ 563 @Override 564 public void visitSelect(JCFieldAccess tree) { 565 if (context == null || !analyzer.lambdaFieldAccessFilter(tree)) { 566 super.visitSelect(tree); 567 } else { 568 int prevPos = make.pos; 569 try { 570 make.at(tree); 571 572 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context; 573 JCTree ltree = lambdaContext.translate(tree); 574 if (ltree != null) { 575 result = ltree; 576 } else { 577 super.visitSelect(tree); 578 } 579 } finally { 580 make.at(prevPos); 581 } 582 } 583 } 584 585 /** 586 * Translate instance creation expressions with implicit enclosing instances 587 * @param tree 588 */ 589 @Override 590 public void visitNewClass(JCNewClass tree) { 591 if (context == null || !analyzer.lambdaNewClassFilter(context, tree)) { 592 super.visitNewClass(tree); 593 } else { 594 int prevPos = make.pos; 595 try { 596 make.at(tree); 597 598 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context; 599 tree = lambdaContext.translate(tree); 600 super.visitNewClass(tree); 601 } finally { 602 make.at(prevPos); 603 } 604 } 605 } 606 607 @Override 608 public void visitVarDef(JCVariableDecl tree) { 609 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context; 610 if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) { 611 tree.init = translate(tree.init); 612 tree.sym = (VarSymbol) lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym); 613 result = tree; 614 } else { 615 super.visitVarDef(tree); 616 } 617 } 618 619 // </editor-fold> 620 621 // <editor-fold defaultstate="collapsed" desc="Translation helper methods"> 622 623 private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) { 624 return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ? 625 makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) : 626 makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally); 627 } 628 629 private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) { 630 Type restype = lambdaMethodDecl.type.getReturnType(); 631 boolean isLambda_void = expr.type.hasTag(VOID); 632 boolean isTarget_void = restype.hasTag(VOID); 633 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type); 634 int prevPos = make.pos; 635 try { 636 if (isTarget_void) { 637 //target is void: 638 // BODY; 639 JCStatement stat = make.at(expr).Exec(expr); 640 return make.Block(0, List.of(stat)); 641 } else if (isLambda_void && isTarget_Void) { 642 //void to Void conversion: 643 // BODY; return null; 644 ListBuffer<JCStatement> stats = new ListBuffer<>(); 645 stats.append(make.at(expr).Exec(expr)); 646 stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType))); 647 return make.Block(0, stats.toList()); 648 } else { 649 //non-void to non-void conversion: 650 // return BODY; 651 return make.at(expr).Block(0, List.of(make.Return(expr))); 652 } 653 } finally { 654 make.at(prevPos); 655 } 656 } 657 658 private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) { 659 final Type restype = lambdaMethodDecl.type.getReturnType(); 660 final boolean isTarget_void = restype.hasTag(VOID); 661 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type); 662 663 class LambdaBodyTranslator extends TreeTranslator { 664 665 @Override 666 public void visitClassDef(JCClassDecl tree) { 667 //do NOT recurse on any inner classes 668 result = tree; 669 } 670 671 @Override 672 public void visitLambda(JCLambda tree) { 673 //do NOT recurse on any nested lambdas 674 result = tree; 675 } 676 677 @Override 678 public void visitReturn(JCReturn tree) { 679 boolean isLambda_void = tree.expr == null; 680 if (isTarget_void && !isLambda_void) { 681 //Void to void conversion: 682 // { TYPE $loc = RET-EXPR; return; } 683 VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym); 684 JCVariableDecl varDef = make.VarDef(loc, tree.expr); 685 result = make.Block(0, List.of(varDef, make.Return(null))); 686 } else { 687 result = tree; 688 } 689 690 } 691 } 692 693 JCBlock trans_block = new LambdaBodyTranslator().translate(block); 694 if (completeNormally && isTarget_Void) { 695 //there's no return statement and the lambda (possibly inferred) 696 //return type is java.lang.Void; emit a synthetic return statement 697 trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType))); 698 } 699 return trans_block; 700 } 701 702 private JCMethodDecl makeDeserializeMethod(Symbol kSym) { 703 ListBuffer<JCCase> cases = new ListBuffer<>(); 704 ListBuffer<JCBreak> breaks = new ListBuffer<>(); 705 for (Map.Entry<String, ListBuffer<JCStatement>> entry : kInfo.deserializeCases.entrySet()) { 706 JCBreak br = make.Break(null); 707 breaks.add(br); 708 List<JCStatement> stmts = entry.getValue().append(br).toList(); 709 cases.add(make.Case(JCCase.STATEMENT, List.of(make.ConstantCaseLabel(make.Literal(entry.getKey()))), stmts, null)); 710 } 711 JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList()); 712 for (JCBreak br : breaks) { 713 br.target = sw; 714 } 715 JCBlock body = make.Block(0L, List.of( 716 sw, 717 make.Throw(makeNewClass( 718 syms.illegalArgumentExceptionType, 719 List.of(make.Literal("Invalid lambda deserialization")))))); 720 JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()), 721 names.deserializeLambda, 722 make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym), 723 List.nil(), 724 List.of(make.VarDef(kInfo.deserParamSym, null)), 725 List.nil(), 726 body, 727 null); 728 deser.sym = kInfo.deserMethodSym; 729 deser.type = kInfo.deserMethodSym.type; 730 //System.err.printf("DESER: '%s'\n", deser); 731 return deser; 732 } 733 734 /** Make an attributed class instance creation expression. 735 * @param ctype The class type. 736 * @param args The constructor arguments. 737 * @param cons The constructor symbol 738 */ 739 JCNewClass makeNewClass(Type ctype, List<JCExpression> args, Symbol cons) { 740 JCNewClass tree = make.NewClass(null, 741 null, make.QualIdent(ctype.tsym), args, null); 742 tree.constructor = cons; 743 tree.type = ctype; 744 return tree; 745 } 746 747 /** Make an attributed class instance creation expression. 748 * @param ctype The class type. 749 * @param args The constructor arguments. 750 */ 751 JCNewClass makeNewClass(Type ctype, List<JCExpression> args) { 752 return makeNewClass(ctype, args, 753 rs.resolveConstructor(null, attrEnv, ctype, TreeInfo.types(args), List.nil())); 754 } 755 756 private void addDeserializationCase(MethodHandleSymbol refSym, Type targetType, MethodSymbol samSym, 757 DiagnosticPosition pos, List<LoadableConstant> staticArgs, MethodType indyType) { 758 String functionalInterfaceClass = classSig(targetType); 759 String functionalInterfaceMethodName = samSym.getSimpleName().toString(); 760 String functionalInterfaceMethodSignature = typeSig(types.erasure(samSym.type)); 761 Symbol baseMethod = refSym.baseSymbol(); 762 Symbol origMethod = baseMethod.baseSymbol(); 763 if (baseMethod != origMethod && origMethod.owner == syms.objectType.tsym) { 764 //the implementation method is a java.lang.Object method transferred to an 765 //interface that does not declare it. Runtime will refer to this method as to 766 //a java.lang.Object method, so do the same: 767 refSym = ((MethodSymbol) origMethod).asHandle(); 768 } 769 String implClass = classSig(types.erasure(refSym.owner.type)); 770 String implMethodName = refSym.getQualifiedName().toString(); 771 String implMethodSignature = typeSig(types.erasure(refSym.type)); 772 773 JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType), 774 make.Literal(refSym.referenceKind())); 775 ListBuffer<JCExpression> serArgs = new ListBuffer<>(); 776 int i = 0; 777 for (Type t : indyType.getParameterTypes()) { 778 List<JCExpression> indexAsArg = new ListBuffer<JCExpression>().append(make.Literal(i)).toList(); 779 List<Type> argTypes = new ListBuffer<Type>().append(syms.intType).toList(); 780 serArgs.add(make.TypeCast(types.erasure(t), deserGetter("getCapturedArg", syms.objectType, argTypes, indexAsArg))); 781 ++i; 782 } 783 JCStatement stmt = make.If( 784 deserTest(deserTest(deserTest(deserTest(deserTest( 785 kindTest, 786 "getFunctionalInterfaceClass", functionalInterfaceClass), 787 "getFunctionalInterfaceMethodName", functionalInterfaceMethodName), 788 "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature), 789 "getImplClass", implClass), 790 "getImplMethodSignature", implMethodSignature), 791 make.Return(makeIndyCall( 792 pos, 793 syms.lambdaMetafactory, 794 names.altMetafactory, 795 staticArgs, indyType, serArgs.toList(), samSym.name)), 796 null); 797 ListBuffer<JCStatement> stmts = kInfo.deserializeCases.get(implMethodName); 798 if (stmts == null) { 799 stmts = new ListBuffer<>(); 800 kInfo.deserializeCases.put(implMethodName, stmts); 801 } 802 /**** 803 System.err.printf("+++++++++++++++++\n"); 804 System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass); 805 System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName); 806 System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature); 807 System.err.printf("*implMethodKind: %d\n", implMethodKind); 808 System.err.printf("*implClass: '%s'\n", implClass); 809 System.err.printf("*implMethodName: '%s'\n", implMethodName); 810 System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature); 811 ****/ 812 stmts.append(stmt); 813 } 814 815 private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) { 816 JCBinary testExpr = make.Binary(JCTree.Tag.EQ, arg1, arg2); 817 testExpr.operator = operators.resolveBinary(testExpr, JCTree.Tag.EQ, argType, argType); 818 testExpr.setType(syms.booleanType); 819 return testExpr; 820 } 821 822 private JCExpression deserTest(JCExpression prev, String func, String lit) { 823 MethodType eqmt = new MethodType(List.of(syms.objectType), syms.booleanType, List.nil(), syms.methodClass); 824 Symbol eqsym = rs.resolveQualifiedMethod(null, attrEnv, syms.objectType, names.equals, List.of(syms.objectType), List.nil()); 825 JCMethodInvocation eqtest = make.Apply( 826 List.nil(), 827 make.Select(deserGetter(func, syms.stringType), eqsym).setType(eqmt), 828 List.of(make.Literal(lit))); 829 eqtest.setType(syms.booleanType); 830 JCBinary compound = make.Binary(JCTree.Tag.AND, prev, eqtest); 831 compound.operator = operators.resolveBinary(compound, JCTree.Tag.AND, syms.booleanType, syms.booleanType); 832 compound.setType(syms.booleanType); 833 return compound; 834 } 835 836 private JCExpression deserGetter(String func, Type type) { 837 return deserGetter(func, type, List.nil(), List.nil()); 838 } 839 840 private JCExpression deserGetter(String func, Type type, List<Type> argTypes, List<JCExpression> args) { 841 MethodType getmt = new MethodType(argTypes, type, List.nil(), syms.methodClass); 842 Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.nil()); 843 return make.Apply( 844 List.nil(), 845 make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt), 846 args).setType(type); 847 } 848 849 /** 850 * Create new synthetic method with given flags, name, type, owner 851 */ 852 private MethodSymbol makePrivateSyntheticMethod(long flags, Name name, Type type, Symbol owner) { 853 return new MethodSymbol(flags | SYNTHETIC | PRIVATE, name, type, owner); 854 } 855 856 /** 857 * Create new synthetic variable with given flags, name, type, owner 858 */ 859 private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) { 860 return new VarSymbol(flags | SYNTHETIC, name, type, owner); 861 } 862 863 /** 864 * Set varargsElement field on a given tree (must be either a new class tree 865 * or a method call tree) 866 */ 867 private void setVarargsIfNeeded(JCTree tree, Type varargsElement) { 868 if (varargsElement != null) { 869 switch (tree.getTag()) { 870 case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break; 871 case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break; 872 case TYPECAST: setVarargsIfNeeded(((JCTypeCast) tree).expr, varargsElement); break; 873 default: throw new AssertionError(); 874 } 875 } 876 } 877 878 /** 879 * Convert method/constructor arguments by inserting appropriate cast 880 * as required by type-erasure - this is needed when bridging a lambda/method 881 * reference, as the bridged signature might require downcast to be compatible 882 * with the generated signature. 883 */ 884 private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) { 885 Assert.check(meth.kind == MTH); 886 List<Type> formals = types.erasure(meth.type).getParameterTypes(); 887 if (varargsElement != null) { 888 Assert.check((meth.flags() & VARARGS) != 0); 889 } 890 return transTypes.translateArgs(args, formals, varargsElement, attrEnv); 891 } 892 893 // </editor-fold> 894 895 /** 896 * Converts a method reference which cannot be used directly into a lambda 897 */ 898 private class MemberReferenceToLambda { 899 900 private final JCMemberReference tree; 901 private final ReferenceTranslationContext localContext; 902 private final Symbol owner; 903 private final ListBuffer<JCExpression> args = new ListBuffer<>(); 904 private final ListBuffer<JCVariableDecl> params = new ListBuffer<>(); 905 906 private JCExpression receiverExpression = null; 907 908 MemberReferenceToLambda(JCMemberReference tree, ReferenceTranslationContext localContext, Symbol owner) { 909 this.tree = tree; 910 this.localContext = localContext; 911 this.owner = owner; 912 } 913 914 JCLambda lambda() { 915 int prevPos = make.pos; 916 try { 917 make.at(tree); 918 919 //body generation - this can be either a method call or a 920 //new instance creation expression, depending on the member reference kind 921 VarSymbol rcvr = addParametersReturnReceiver(); 922 JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE) 923 ? expressionInvoke(rcvr) 924 : expressionNew(); 925 926 JCLambda slam = make.Lambda(params.toList(), expr); 927 slam.target = tree.target; 928 slam.type = tree.type; 929 slam.pos = tree.pos; 930 return slam; 931 } finally { 932 make.at(prevPos); 933 } 934 } 935 936 /** 937 * Generate the parameter list for the converted member reference. 938 * 939 * @return The receiver variable symbol, if any 940 */ 941 VarSymbol addParametersReturnReceiver() { 942 Type samDesc = localContext.bridgedRefSig(); 943 List<Type> samPTypes = samDesc.getParameterTypes(); 944 List<Type> descPTypes = tree.getDescriptorType(types).getParameterTypes(); 945 946 // Determine the receiver, if any 947 VarSymbol rcvr; 948 switch (tree.kind) { 949 case BOUND: 950 // The receiver is explicit in the method reference 951 rcvr = addParameter("rec$", tree.getQualifierExpression().type, false); 952 receiverExpression = attr.makeNullCheck(tree.getQualifierExpression()); 953 break; 954 case UNBOUND: 955 // The receiver is the first parameter, extract it and 956 // adjust the SAM and unerased type lists accordingly 957 rcvr = addParameter("rec$", samDesc.getParameterTypes().head, false); 958 samPTypes = samPTypes.tail; 959 descPTypes = descPTypes.tail; 960 break; 961 default: 962 rcvr = null; 963 break; 964 } 965 List<Type> implPTypes = tree.sym.type.getParameterTypes(); 966 int implSize = implPTypes.size(); 967 int samSize = samPTypes.size(); 968 // Last parameter to copy from referenced method, exclude final var args 969 int last = localContext.needsVarArgsConversion() ? implSize - 1 : implSize; 970 971 // Failsafe -- assure match-up 972 boolean checkForIntersection = tree.varargsElement != null || implSize == descPTypes.size(); 973 974 // Use parameter types of the implementation method unless the unerased 975 // SAM parameter type is an intersection type, in that case use the 976 // erased SAM parameter type so that the supertype relationship 977 // the implementation method parameters is not obscured. 978 // Note: in this loop, the lists implPTypes, samPTypes, and descPTypes 979 // are used as pointers to the current parameter type information 980 // and are thus not usable afterwards. 981 for (int i = 0; implPTypes.nonEmpty() && i < last; ++i) { 982 // By default use the implementation method parameter type 983 Type parmType = implPTypes.head; 984 if (checkForIntersection) { 985 if (descPTypes.head.getKind() == TypeKind.INTERSECTION) { 986 parmType = samPTypes.head; 987 } 988 // If the unerased parameter type is a type variable whose 989 // bound is an intersection (eg. <T extends A & B>) then 990 // use the SAM parameter type 991 if (descPTypes.head.getKind() == TypeKind.TYPEVAR) { 992 TypeVar tv = (TypeVar) descPTypes.head; 993 if (tv.getUpperBound().getKind() == TypeKind.INTERSECTION) { 994 parmType = samPTypes.head; 995 } 996 } 997 } 998 addParameter("x$" + i, parmType, true); 999 1000 // Advance to the next parameter 1001 implPTypes = implPTypes.tail; 1002 samPTypes = samPTypes.tail; 1003 descPTypes = descPTypes.tail; 1004 } 1005 // Flatten out the var args 1006 for (int i = last; i < samSize; ++i) { 1007 addParameter("xva$" + i, tree.varargsElement, true); 1008 } 1009 1010 return rcvr; 1011 } 1012 1013 JCExpression getReceiverExpression() { 1014 return receiverExpression; 1015 } 1016 1017 private JCExpression makeReceiver(VarSymbol rcvr) { 1018 if (rcvr == null) return null; 1019 JCExpression rcvrExpr = make.Ident(rcvr); 1020 boolean protAccess = 1021 isProtectedInSuperClassOfEnclosingClassInOtherPackage(tree.sym, owner); 1022 Type rcvrType = tree.ownerAccessible && !protAccess ? tree.sym.enclClass().type 1023 : tree.expr.type; 1024 if (rcvrType == syms.arrayClass.type) { 1025 // Map the receiver type to the actually type, not just "array" 1026 rcvrType = tree.getQualifierExpression().type; 1027 } 1028 if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) { 1029 rcvrExpr = make.TypeCast(make.Type(rcvrType), rcvrExpr).setType(rcvrType); 1030 } 1031 return rcvrExpr; 1032 } 1033 1034 /** 1035 * determine the receiver of the method call - the receiver can 1036 * be a type qualifier, the synthetic receiver parameter or 'super'. 1037 */ 1038 private JCExpression expressionInvoke(VarSymbol rcvr) { 1039 JCExpression qualifier = 1040 (rcvr != null) ? 1041 makeReceiver(rcvr) : 1042 tree.getQualifierExpression(); 1043 1044 //create the qualifier expression 1045 JCFieldAccess select = make.Select(qualifier, tree.sym.name); 1046 select.sym = tree.sym; 1047 select.type = tree.sym.erasure(types); 1048 1049 //create the method call expression 1050 JCExpression apply = make.Apply(List.nil(), select, 1051 convertArgs(tree.sym, args.toList(), tree.varargsElement)). 1052 setType(tree.sym.erasure(types).getReturnType()); 1053 1054 apply = transTypes.coerce(attrEnv, apply, 1055 types.erasure(localContext.tree.referentType.getReturnType())); 1056 1057 setVarargsIfNeeded(apply, tree.varargsElement); 1058 return apply; 1059 } 1060 1061 /** 1062 * Lambda body to use for a 'new'. 1063 */ 1064 private JCExpression expressionNew() { 1065 if (tree.kind == ReferenceKind.ARRAY_CTOR) { 1066 //create the array creation expression 1067 JCNewArray newArr = make.NewArray( 1068 make.Type(types.elemtype(tree.getQualifierExpression().type)), 1069 List.of(make.Ident(params.first())), 1070 null); 1071 newArr.type = tree.getQualifierExpression().type; 1072 return newArr; 1073 } else { 1074 //create the instance creation expression 1075 //note that method reference syntax does not allow an explicit 1076 //enclosing class (so the enclosing class is null) 1077 // but this may need to be patched up later with the proxy for the outer this 1078 JCNewClass newClass = make.NewClass(null, 1079 List.nil(), 1080 make.Type(tree.getQualifierExpression().type), 1081 convertArgs(tree.sym, args.toList(), tree.varargsElement), 1082 null); 1083 newClass.constructor = tree.sym; 1084 newClass.constructorType = tree.sym.erasure(types); 1085 newClass.type = tree.getQualifierExpression().type; 1086 setVarargsIfNeeded(newClass, tree.varargsElement); 1087 return newClass; 1088 } 1089 } 1090 1091 private VarSymbol addParameter(String name, Type p, boolean genArg) { 1092 VarSymbol vsym = new VarSymbol(PARAMETER | SYNTHETIC, names.fromString(name), p, owner); 1093 vsym.pos = tree.pos; 1094 params.append(make.VarDef(vsym, null)); 1095 if (genArg) { 1096 args.append(make.Ident(vsym)); 1097 } 1098 return vsym; 1099 } 1100 } 1101 1102 private MethodType typeToMethodType(Type mt) { 1103 Type type = types.erasure(mt); 1104 return new MethodType(type.getParameterTypes(), 1105 type.getReturnType(), 1106 type.getThrownTypes(), 1107 syms.methodClass); 1108 } 1109 1110 /** 1111 * Generate an indy method call to the meta factory 1112 */ 1113 private JCExpression makeMetafactoryIndyCall(TranslationContext<?> context, 1114 MethodHandleSymbol refSym, List<JCExpression> indy_args) { 1115 JCFunctionalExpression tree = context.tree; 1116 //determine the static bsm args 1117 MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.target.tsym); 1118 List<LoadableConstant> staticArgs = List.of( 1119 typeToMethodType(samSym.type), 1120 refSym.asHandle(), 1121 typeToMethodType(tree.getDescriptorType(types))); 1122 1123 //computed indy arg types 1124 ListBuffer<Type> indy_args_types = new ListBuffer<>(); 1125 for (JCExpression arg : indy_args) { 1126 indy_args_types.append(arg.type); 1127 } 1128 1129 //finally, compute the type of the indy call 1130 MethodType indyType = new MethodType(indy_args_types.toList(), 1131 tree.type, 1132 List.nil(), 1133 syms.methodClass); 1134 1135 Name metafactoryName = context.needsAltMetafactory() ? 1136 names.altMetafactory : names.metafactory; 1137 1138 if (context.needsAltMetafactory()) { 1139 ListBuffer<Type> markers = new ListBuffer<>(); 1140 List<Type> targets = tree.target.isIntersection() ? 1141 types.directSupertypes(tree.target) : 1142 List.nil(); 1143 for (Type t : targets) { 1144 t = types.erasure(t); 1145 if (t.tsym != syms.serializableType.tsym && 1146 t.tsym != tree.type.tsym && 1147 t.tsym != syms.objectType.tsym) { 1148 markers.append(t); 1149 } 1150 } 1151 int flags = context.isSerializable() ? FLAG_SERIALIZABLE : 0; 1152 boolean hasMarkers = markers.nonEmpty(); 1153 boolean hasBridges = context.bridges.nonEmpty(); 1154 if (hasMarkers) { 1155 flags |= FLAG_MARKERS; 1156 } 1157 if (hasBridges) { 1158 flags |= FLAG_BRIDGES; 1159 } 1160 staticArgs = staticArgs.append(LoadableConstant.Int(flags)); 1161 if (hasMarkers) { 1162 staticArgs = staticArgs.append(LoadableConstant.Int(markers.length())); 1163 staticArgs = staticArgs.appendList(List.convert(LoadableConstant.class, markers.toList())); 1164 } 1165 if (hasBridges) { 1166 staticArgs = staticArgs.append(LoadableConstant.Int(context.bridges.length() - 1)); 1167 for (Symbol s : context.bridges) { 1168 Type s_erasure = s.erasure(types); 1169 if (!types.isSameType(s_erasure, samSym.erasure(types))) { 1170 staticArgs = staticArgs.append(((MethodType)s.erasure(types))); 1171 } 1172 } 1173 } 1174 if (context.isSerializable()) { 1175 int prevPos = make.pos; 1176 try { 1177 make.at(kInfo.clazz); 1178 addDeserializationCase(refSym, tree.type, samSym, 1179 tree, staticArgs, indyType); 1180 } finally { 1181 make.at(prevPos); 1182 } 1183 } 1184 } 1185 1186 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args, samSym.name); 1187 } 1188 1189 /** 1190 * Generate an indy method call with given name, type and static bootstrap 1191 * arguments types 1192 */ 1193 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName, 1194 List<LoadableConstant> staticArgs, MethodType indyType, List<JCExpression> indyArgs, 1195 Name methName) { 1196 int prevPos = make.pos; 1197 try { 1198 make.at(pos); 1199 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType, 1200 syms.stringType, 1201 syms.methodTypeType).appendList(staticArgs.map(types::constantType)); 1202 1203 MethodSymbol bsm = rs.resolveInternalMethod(pos, attrEnv, site, 1204 bsmName, bsm_staticArgs, List.nil()); 1205 1206 DynamicMethodSymbol dynSym = 1207 new DynamicMethodSymbol(methName, 1208 syms.noSymbol, 1209 bsm.asHandle(), 1210 indyType, 1211 staticArgs.toArray(new LoadableConstant[staticArgs.length()])); 1212 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName); 1213 DynamicMethodSymbol existing = kInfo.dynMethSyms.putIfAbsent( 1214 dynSym.poolKey(types), dynSym); 1215 qualifier.sym = existing != null ? existing : dynSym; 1216 qualifier.type = indyType.getReturnType(); 1217 1218 JCMethodInvocation proxyCall = make.Apply(List.nil(), qualifier, indyArgs); 1219 proxyCall.type = indyType.getReturnType(); 1220 return proxyCall; 1221 } finally { 1222 make.at(prevPos); 1223 } 1224 } 1225 1226 // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer"> 1227 /** 1228 * This visitor collects information about translation of a lambda expression. 1229 * More specifically, it keeps track of the enclosing contexts and captured locals 1230 * accessed by the lambda being translated (as well as other useful info). 1231 * It also translates away problems for LambdaToMethod. 1232 */ 1233 class LambdaAnalyzerPreprocessor extends TreeTranslator { 1234 1235 /** the frame stack - used to reconstruct translation info about enclosing scopes */ 1236 private List<Frame> frameStack; 1237 1238 /** 1239 * keep the count of lambda expression (used to generate unambiguous 1240 * names) 1241 */ 1242 private int lambdaCount = 0; 1243 1244 /** 1245 * List of types undergoing construction via explicit constructor chaining. 1246 */ 1247 private List<ClassSymbol> typesUnderConstruction; 1248 1249 /** 1250 * keep the count of lambda expression defined in given context (used to 1251 * generate unambiguous names for serializable lambdas) 1252 */ 1253 private class SyntheticMethodNameCounter { 1254 private Map<String, Integer> map = new HashMap<>(); 1255 int getIndex(StringBuilder buf) { 1256 String temp = buf.toString(); 1257 Integer count = map.get(temp); 1258 if (count == null) { 1259 count = 0; 1260 } 1261 ++count; 1262 map.put(temp, count); 1263 return count; 1264 } 1265 } 1266 private SyntheticMethodNameCounter syntheticMethodNameCounts = 1267 new SyntheticMethodNameCounter(); 1268 1269 private Map<Symbol, JCClassDecl> localClassDefs; 1270 1271 /** 1272 * maps for fake clinit symbols to be used as owners of lambda occurring in 1273 * a static var init context 1274 */ 1275 private Map<ClassSymbol, Symbol> clinits = new HashMap<>(); 1276 1277 private JCClassDecl analyzeAndPreprocessClass(JCClassDecl tree) { 1278 frameStack = List.nil(); 1279 typesUnderConstruction = List.nil(); 1280 localClassDefs = new HashMap<>(); 1281 return translate(tree); 1282 } 1283 1284 @Override 1285 public void visitApply(JCMethodInvocation tree) { 1286 List<ClassSymbol> previousNascentTypes = typesUnderConstruction; 1287 try { 1288 Name methName = TreeInfo.name(tree.meth); 1289 if (methName == names._this || methName == names._super) { 1290 typesUnderConstruction = typesUnderConstruction.prepend(currentClass()); 1291 } 1292 super.visitApply(tree); 1293 } finally { 1294 typesUnderConstruction = previousNascentTypes; 1295 } 1296 } 1297 // where 1298 private ClassSymbol currentClass() { 1299 for (Frame frame : frameStack) { 1300 if (frame.tree.hasTag(JCTree.Tag.CLASSDEF)) { 1301 JCClassDecl cdef = (JCClassDecl) frame.tree; 1302 return cdef.sym; 1303 } 1304 } 1305 return null; 1306 } 1307 1308 @Override 1309 public void visitBlock(JCBlock tree) { 1310 List<Frame> prevStack = frameStack; 1311 try { 1312 if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) { 1313 frameStack = frameStack.prepend(new Frame(tree)); 1314 } 1315 super.visitBlock(tree); 1316 } 1317 finally { 1318 frameStack = prevStack; 1319 } 1320 } 1321 1322 @Override 1323 public void visitClassDef(JCClassDecl tree) { 1324 List<Frame> prevStack = frameStack; 1325 int prevLambdaCount = lambdaCount; 1326 SyntheticMethodNameCounter prevSyntheticMethodNameCounts = 1327 syntheticMethodNameCounts; 1328 Map<ClassSymbol, Symbol> prevClinits = clinits; 1329 DiagnosticSource prevSource = log.currentSource(); 1330 try { 1331 log.useSource(tree.sym.sourcefile); 1332 lambdaCount = 0; 1333 syntheticMethodNameCounts = new SyntheticMethodNameCounter(); 1334 prevClinits = new HashMap<>(); 1335 if (tree.sym.owner.kind == MTH) { 1336 localClassDefs.put(tree.sym, tree); 1337 } 1338 if (directlyEnclosingLambda() != null) { 1339 tree.sym.owner = owner(); 1340 if (tree.sym.hasOuterInstance()) { 1341 //if a class is defined within a lambda, the lambda must capture 1342 //its enclosing instance (if any) 1343 TranslationContext<?> localContext = context(); 1344 final TypeSymbol outerInstanceSymbol = tree.sym.type.getEnclosingType().tsym; 1345 while (localContext != null && !localContext.owner.isStatic()) { 1346 if (localContext.tree.hasTag(LAMBDA)) { 1347 JCTree block = capturedDecl(localContext.depth, outerInstanceSymbol); 1348 if (block == null) break; 1349 ((LambdaTranslationContext)localContext) 1350 .addSymbol(outerInstanceSymbol, CAPTURED_THIS); 1351 } 1352 localContext = localContext.prev; 1353 } 1354 } 1355 } 1356 frameStack = frameStack.prepend(new Frame(tree)); 1357 super.visitClassDef(tree); 1358 } 1359 finally { 1360 log.useSource(prevSource.getFile()); 1361 frameStack = prevStack; 1362 lambdaCount = prevLambdaCount; 1363 syntheticMethodNameCounts = prevSyntheticMethodNameCounts; 1364 clinits = prevClinits; 1365 } 1366 } 1367 1368 @Override 1369 public void visitIdent(JCIdent tree) { 1370 if (context() != null && lambdaIdentSymbolFilter(tree.sym)) { 1371 if (tree.sym.kind == VAR && 1372 tree.sym.owner.kind == MTH && 1373 tree.type.constValue() == null) { 1374 TranslationContext<?> localContext = context(); 1375 while (localContext != null) { 1376 if (localContext.tree.getTag() == LAMBDA) { 1377 JCTree block = capturedDecl(localContext.depth, tree.sym); 1378 if (block == null) break; 1379 ((LambdaTranslationContext)localContext) 1380 .addSymbol(tree.sym, CAPTURED_VAR); 1381 } 1382 localContext = localContext.prev; 1383 } 1384 } else if (tree.sym.owner.kind == TYP) { 1385 TranslationContext<?> localContext = context(); 1386 while (localContext != null && !localContext.owner.isStatic()) { 1387 if (localContext.tree.hasTag(LAMBDA)) { 1388 JCTree block = capturedDecl(localContext.depth, tree.sym); 1389 if (block == null) break; 1390 switch (block.getTag()) { 1391 case CLASSDEF: 1392 JCClassDecl cdecl = (JCClassDecl)block; 1393 ((LambdaTranslationContext)localContext) 1394 .addSymbol(cdecl.sym, CAPTURED_THIS); 1395 break; 1396 default: 1397 Assert.error("bad block kind"); 1398 } 1399 } 1400 localContext = localContext.prev; 1401 } 1402 } 1403 } 1404 super.visitIdent(tree); 1405 } 1406 1407 @Override 1408 public void visitLambda(JCLambda tree) { 1409 analyzeLambda(tree, "lambda.stat"); 1410 } 1411 1412 private void analyzeLambda(JCLambda tree, JCExpression methodReferenceReceiver) { 1413 // Translation of the receiver expression must occur first 1414 JCExpression rcvr = translate(methodReferenceReceiver); 1415 LambdaTranslationContext context = analyzeLambda(tree, "mref.stat.1"); 1416 if (rcvr != null) { 1417 context.methodReferenceReceiver = rcvr; 1418 } 1419 } 1420 1421 private LambdaTranslationContext analyzeLambda(JCLambda tree, String statKey) { 1422 List<Frame> prevStack = frameStack; 1423 try { 1424 LambdaTranslationContext context = new LambdaTranslationContext(tree); 1425 frameStack = frameStack.prepend(new Frame(tree)); 1426 for (JCVariableDecl param : tree.params) { 1427 context.addSymbol(param.sym, PARAM); 1428 frameStack.head.addLocal(param.sym); 1429 } 1430 contextMap.put(tree, context); 1431 super.visitLambda(tree); 1432 context.complete(); 1433 if (dumpLambdaToMethodStats) { 1434 log.note(tree, diags.noteKey(statKey, context.needsAltMetafactory(), context.translatedSym)); 1435 } 1436 return context; 1437 } 1438 finally { 1439 frameStack = prevStack; 1440 } 1441 } 1442 1443 @Override 1444 public void visitMethodDef(JCMethodDecl tree) { 1445 List<Frame> prevStack = frameStack; 1446 try { 1447 frameStack = frameStack.prepend(new Frame(tree)); 1448 super.visitMethodDef(tree); 1449 } 1450 finally { 1451 frameStack = prevStack; 1452 } 1453 } 1454 1455 @Override 1456 public void visitNewClass(JCNewClass tree) { 1457 TypeSymbol def = tree.type.tsym; 1458 boolean inReferencedClass = currentlyInClass(def); 1459 boolean isLocal = def.isDirectlyOrIndirectlyLocal(); 1460 if ((inReferencedClass && isLocal || lambdaNewClassFilter(context(), tree))) { 1461 TranslationContext<?> localContext = context(); 1462 final TypeSymbol outerInstanceSymbol = tree.type.getEnclosingType().tsym; 1463 while (localContext != null && !localContext.owner.isStatic()) { 1464 if (localContext.tree.hasTag(LAMBDA)) { 1465 if (outerInstanceSymbol != null) { 1466 JCTree block = capturedDecl(localContext.depth, outerInstanceSymbol); 1467 if (block == null) break; 1468 } 1469 ((LambdaTranslationContext)localContext) 1470 .addSymbol(outerInstanceSymbol, CAPTURED_THIS); 1471 } 1472 localContext = localContext.prev; 1473 } 1474 } 1475 super.visitNewClass(tree); 1476 if (context() != null && !inReferencedClass && isLocal) { 1477 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context(); 1478 captureLocalClassDefs(def, lambdaContext); 1479 } 1480 } 1481 //where 1482 void captureLocalClassDefs(Symbol csym, final LambdaTranslationContext lambdaContext) { 1483 JCClassDecl localCDef = localClassDefs.get(csym); 1484 if (localCDef != null && lambdaContext.freeVarProcessedLocalClasses.add(csym)) { 1485 BasicFreeVarCollector fvc = lower.new BasicFreeVarCollector() { 1486 @Override 1487 void addFreeVars(ClassSymbol c) { 1488 captureLocalClassDefs(c, lambdaContext); 1489 } 1490 @Override 1491 void visitSymbol(Symbol sym) { 1492 if (sym.kind == VAR && 1493 sym.owner.kind == MTH && 1494 ((VarSymbol)sym).getConstValue() == null) { 1495 TranslationContext<?> localContext = context(); 1496 while (localContext != null) { 1497 if (localContext.tree.getTag() == LAMBDA) { 1498 JCTree block = capturedDecl(localContext.depth, sym); 1499 if (block == null) break; 1500 ((LambdaTranslationContext)localContext).addSymbol(sym, CAPTURED_VAR); 1501 } 1502 localContext = localContext.prev; 1503 } 1504 } 1505 } 1506 }; 1507 fvc.scan(localCDef); 1508 } 1509 } 1510 //where 1511 boolean currentlyInClass(Symbol csym) { 1512 for (Frame frame : frameStack) { 1513 if (frame.tree.hasTag(JCTree.Tag.CLASSDEF)) { 1514 JCClassDecl cdef = (JCClassDecl) frame.tree; 1515 if (cdef.sym == csym) { 1516 return true; 1517 } 1518 } 1519 } 1520 return false; 1521 } 1522 1523 /** 1524 * Method references to local class constructors, may, if the local 1525 * class references local variables, have implicit constructor 1526 * parameters added in Lower; As a result, the invokedynamic bootstrap 1527 * information added in the LambdaToMethod pass will have the wrong 1528 * signature. Hooks between Lower and LambdaToMethod have been added to 1529 * handle normal "new" in this case. This visitor converts potentially 1530 * affected method references into a lambda containing a normal 1531 * expression. 1532 * 1533 * @param tree 1534 */ 1535 @Override 1536 public void visitReference(JCMemberReference tree) { 1537 ReferenceTranslationContext rcontext = new ReferenceTranslationContext(tree); 1538 contextMap.put(tree, rcontext); 1539 if (rcontext.needsConversionToLambda()) { 1540 // Convert to a lambda, and process as such 1541 MemberReferenceToLambda conv = new MemberReferenceToLambda(tree, rcontext, owner()); 1542 analyzeLambda(conv.lambda(), conv.getReceiverExpression()); 1543 } else { 1544 super.visitReference(tree); 1545 if (dumpLambdaToMethodStats) { 1546 log.note(tree, Notes.MrefStat(rcontext.needsAltMetafactory(), null)); 1547 } 1548 } 1549 } 1550 1551 @Override 1552 public void visitSelect(JCFieldAccess tree) { 1553 if (context() != null && tree.sym.kind == VAR && 1554 (tree.sym.name == names._this || 1555 tree.sym.name == names._super)) { 1556 // A select of this or super means, if we are in a lambda, 1557 // we much have an instance context 1558 TranslationContext<?> localContext = context(); 1559 while (localContext != null && !localContext.owner.isStatic()) { 1560 if (localContext.tree.hasTag(LAMBDA)) { 1561 JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym); 1562 if (clazz == null) break; 1563 ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS); 1564 } 1565 localContext = localContext.prev; 1566 } 1567 } 1568 super.visitSelect(tree); 1569 } 1570 1571 @Override 1572 public void visitVarDef(JCVariableDecl tree) { 1573 TranslationContext<?> context = context(); 1574 if (context != null && context instanceof LambdaTranslationContext lambdaContext) { 1575 if (frameStack.head.tree.hasTag(LAMBDA)) { 1576 lambdaContext.addSymbol(tree.sym, LOCAL_VAR); 1577 } 1578 // Check for type variables (including as type arguments). 1579 // If they occur within class nested in a lambda, mark for erasure 1580 Type type = tree.sym.asType(); 1581 } 1582 1583 List<Frame> prevStack = frameStack; 1584 try { 1585 if (tree.sym.owner.kind == MTH) { 1586 frameStack.head.addLocal(tree.sym); 1587 } 1588 frameStack = frameStack.prepend(new Frame(tree)); 1589 super.visitVarDef(tree); 1590 } 1591 finally { 1592 frameStack = prevStack; 1593 } 1594 } 1595 1596 /** 1597 * Return a valid owner given the current declaration stack 1598 * (required to skip synthetic lambda symbols) 1599 */ 1600 private Symbol owner() { 1601 return owner(false); 1602 } 1603 1604 @SuppressWarnings("fallthrough") 1605 private Symbol owner(boolean skipLambda) { 1606 List<Frame> frameStack2 = frameStack; 1607 while (frameStack2.nonEmpty()) { 1608 switch (frameStack2.head.tree.getTag()) { 1609 case VARDEF: 1610 if (((JCVariableDecl)frameStack2.head.tree).sym.isDirectlyOrIndirectlyLocal()) { 1611 frameStack2 = frameStack2.tail; 1612 break; 1613 } 1614 JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree; 1615 return initSym(cdecl.sym, 1616 ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC); 1617 case BLOCK: 1618 JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree; 1619 return initSym(cdecl2.sym, 1620 ((JCBlock)frameStack2.head.tree).flags & STATIC); 1621 case CLASSDEF: 1622 return ((JCClassDecl)frameStack2.head.tree).sym; 1623 case METHODDEF: 1624 return ((JCMethodDecl)frameStack2.head.tree).sym; 1625 case LAMBDA: 1626 if (!skipLambda) 1627 return ((LambdaTranslationContext)contextMap 1628 .get(frameStack2.head.tree)).translatedSym; 1629 default: 1630 frameStack2 = frameStack2.tail; 1631 } 1632 } 1633 Assert.error(); 1634 return null; 1635 } 1636 1637 private Symbol initSym(ClassSymbol csym, long flags) { 1638 boolean isStatic = (flags & STATIC) != 0; 1639 if (isStatic) { 1640 /* static clinits are generated in Gen, so we need to use a fake 1641 * one. Attr creates a fake clinit method while attributing 1642 * lambda expressions used as initializers of static fields, so 1643 * let's use that one. 1644 */ 1645 MethodSymbol clinit = attr.removeClinit(csym); 1646 if (clinit != null) { 1647 clinits.put(csym, clinit); 1648 return clinit; 1649 } 1650 1651 /* if no clinit is found at Attr, then let's try at clinits. 1652 */ 1653 clinit = (MethodSymbol)clinits.get(csym); 1654 if (clinit == null) { 1655 /* no luck, let's create a new one 1656 */ 1657 clinit = makePrivateSyntheticMethod(STATIC, 1658 names.clinit, 1659 new MethodType(List.nil(), syms.voidType, 1660 List.nil(), syms.methodClass), 1661 csym); 1662 clinits.put(csym, clinit); 1663 } 1664 return clinit; 1665 } else { 1666 //get the first constructor and treat it as the instance init sym 1667 for (Symbol s : csym.members_field.getSymbolsByName(names.init)) { 1668 return s; 1669 } 1670 } 1671 Assert.error("init not found"); 1672 return null; 1673 } 1674 1675 private JCTree directlyEnclosingLambda() { 1676 if (frameStack.isEmpty()) { 1677 return null; 1678 } 1679 List<Frame> frameStack2 = frameStack; 1680 while (frameStack2.nonEmpty()) { 1681 switch (frameStack2.head.tree.getTag()) { 1682 case CLASSDEF: 1683 case METHODDEF: 1684 return null; 1685 case LAMBDA: 1686 return frameStack2.head.tree; 1687 default: 1688 frameStack2 = frameStack2.tail; 1689 } 1690 } 1691 Assert.error(); 1692 return null; 1693 } 1694 1695 private boolean inClassWithinLambda() { 1696 if (frameStack.isEmpty()) { 1697 return false; 1698 } 1699 List<Frame> frameStack2 = frameStack; 1700 boolean classFound = false; 1701 while (frameStack2.nonEmpty()) { 1702 switch (frameStack2.head.tree.getTag()) { 1703 case LAMBDA: 1704 return classFound; 1705 case CLASSDEF: 1706 classFound = true; 1707 frameStack2 = frameStack2.tail; 1708 break; 1709 default: 1710 frameStack2 = frameStack2.tail; 1711 } 1712 } 1713 // No lambda 1714 return false; 1715 } 1716 1717 /** 1718 * Return the declaration corresponding to a symbol in the enclosing 1719 * scope; the depth parameter is used to filter out symbols defined 1720 * in nested scopes (which do not need to undergo capture). 1721 */ 1722 private JCTree capturedDecl(int depth, Symbol sym) { 1723 int currentDepth = frameStack.size() - 1; 1724 for (Frame block : frameStack) { 1725 switch (block.tree.getTag()) { 1726 case CLASSDEF: 1727 ClassSymbol clazz = ((JCClassDecl)block.tree).sym; 1728 if (clazz.isSubClass(sym, types) || sym.isMemberOf(clazz, types)) { 1729 return currentDepth > depth ? null : block.tree; 1730 } 1731 break; 1732 case VARDEF: 1733 if ((((JCVariableDecl)block.tree).sym == sym && 1734 sym.owner.kind == MTH) || //only locals are captured 1735 (block.locals != null && block.locals.contains(sym))) { 1736 return currentDepth > depth ? null : block.tree; 1737 } 1738 break; 1739 case BLOCK: 1740 case METHODDEF: 1741 case LAMBDA: 1742 if (block.locals != null && block.locals.contains(sym)) { 1743 return currentDepth > depth ? null : block.tree; 1744 } 1745 break; 1746 default: 1747 Assert.error("bad decl kind " + block.tree.getTag()); 1748 } 1749 currentDepth--; 1750 } 1751 return null; 1752 } 1753 1754 private TranslationContext<?> context() { 1755 for (Frame frame : frameStack) { 1756 TranslationContext<?> context = contextMap.get(frame.tree); 1757 if (context != null) { 1758 return context; 1759 } 1760 } 1761 return null; 1762 } 1763 1764 /** 1765 * This is used to filter out those identifiers that needs to be adjusted 1766 * when translating away lambda expressions 1767 */ 1768 private boolean lambdaIdentSymbolFilter(Symbol sym) { 1769 return (sym.kind == VAR || sym.kind == MTH) 1770 && !sym.isStatic() 1771 && sym.name != names.init; 1772 } 1773 1774 /** 1775 * This is used to filter out those select nodes that need to be adjusted 1776 * when translating away lambda expressions - at the moment, this is the 1777 * set of nodes that select `this' (qualified this) 1778 */ 1779 private boolean lambdaFieldAccessFilter(JCFieldAccess fAccess) { 1780 return (context instanceof LambdaTranslationContext lambdaContext) 1781 && !fAccess.sym.isStatic() 1782 && fAccess.name == names._this 1783 && (fAccess.sym.owner.kind == TYP) 1784 && !lambdaContext.translatedSymbols.get(CAPTURED_OUTER_THIS).isEmpty(); 1785 } 1786 1787 /** 1788 * This is used to filter out those new class expressions that need to 1789 * be qualified with an enclosing tree 1790 */ 1791 private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) { 1792 if (context != null 1793 && tree.encl == null 1794 && tree.def == null 1795 && !tree.type.getEnclosingType().hasTag(NONE)) { 1796 Type encl = tree.type.getEnclosingType(); 1797 Type current = context.owner.enclClass().type; 1798 while (!current.hasTag(NONE)) { 1799 if (current.tsym.isSubClass(encl.tsym, types)) { 1800 return true; 1801 } 1802 current = current.getEnclosingType(); 1803 } 1804 return false; 1805 } else { 1806 return false; 1807 } 1808 } 1809 1810 private class Frame { 1811 final JCTree tree; 1812 List<Symbol> locals; 1813 1814 public Frame(JCTree tree) { 1815 this.tree = tree; 1816 } 1817 1818 void addLocal(Symbol sym) { 1819 if (locals == null) { 1820 locals = List.nil(); 1821 } 1822 locals = locals.prepend(sym); 1823 } 1824 } 1825 1826 /** 1827 * This class is used to store important information regarding translation of 1828 * lambda expression/method references (see subclasses). 1829 */ 1830 abstract class TranslationContext<T extends JCFunctionalExpression> { 1831 1832 /** the underlying (untranslated) tree */ 1833 final T tree; 1834 1835 /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */ 1836 final Symbol owner; 1837 1838 /** the depth of this lambda expression in the frame stack */ 1839 final int depth; 1840 1841 /** the enclosing translation context (set for nested lambdas/mref) */ 1842 final TranslationContext<?> prev; 1843 1844 /** list of methods to be bridged by the meta-factory */ 1845 final List<Symbol> bridges; 1846 1847 TranslationContext(T tree) { 1848 this.tree = tree; 1849 this.owner = owner(true); 1850 this.depth = frameStack.size() - 1; 1851 this.prev = context(); 1852 ClassSymbol csym = 1853 types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.target, ABSTRACT | INTERFACE); 1854 this.bridges = types.functionalInterfaceBridges(csym); 1855 } 1856 1857 /** does this functional expression need to be created using alternate metafactory? */ 1858 boolean needsAltMetafactory() { 1859 return tree.target.isIntersection() || 1860 isSerializable() || 1861 bridges.length() > 1; 1862 } 1863 1864 /** does this functional expression require serialization support? */ 1865 boolean isSerializable() { 1866 if (forceSerializable) { 1867 return true; 1868 } 1869 return types.asSuper(tree.target, syms.serializableType.tsym) != null; 1870 } 1871 1872 /** 1873 * @return Name of the enclosing method to be folded into synthetic 1874 * method name 1875 */ 1876 String enclosingMethodName() { 1877 return syntheticMethodNameComponent(owner.name); 1878 } 1879 1880 /** 1881 * @return Method name in a form that can be folded into a 1882 * component of a synthetic method name 1883 */ 1884 String syntheticMethodNameComponent(Name name) { 1885 if (name == null) { 1886 return "null"; 1887 } 1888 String methodName = name.toString(); 1889 if (methodName.equals("<clinit>")) { 1890 methodName = "static"; 1891 } else if (methodName.equals("<init>")) { 1892 methodName = "new"; 1893 } 1894 return methodName; 1895 } 1896 } 1897 1898 /** 1899 * This class retains all the useful information about a lambda expression; 1900 * the contents of this class are filled by the LambdaAnalyzer visitor, 1901 * and the used by the main translation routines in order to adjust references 1902 * to captured locals/members, etc. 1903 */ 1904 class LambdaTranslationContext extends TranslationContext<JCLambda> { 1905 1906 /** variable in the enclosing context to which this lambda is assigned */ 1907 final Symbol self; 1908 1909 /** variable in the enclosing context to which this lambda is assigned */ 1910 final Symbol assignedTo; 1911 1912 Map<LambdaSymbolKind, Map<Symbol, Symbol>> translatedSymbols; 1913 1914 /** the synthetic symbol for the method hoisting the translated lambda */ 1915 MethodSymbol translatedSym; 1916 1917 List<JCVariableDecl> syntheticParams; 1918 1919 /** 1920 * to prevent recursion, track local classes processed 1921 */ 1922 final Set<Symbol> freeVarProcessedLocalClasses; 1923 1924 /** 1925 * For method references converted to lambdas. The method 1926 * reference receiver expression. Must be treated like a captured 1927 * variable. 1928 */ 1929 JCExpression methodReferenceReceiver; 1930 1931 LambdaTranslationContext(JCLambda tree) { 1932 super(tree); 1933 Frame frame = frameStack.head; 1934 switch (frame.tree.getTag()) { 1935 case VARDEF: 1936 assignedTo = self = ((JCVariableDecl) frame.tree).sym; 1937 break; 1938 case ASSIGN: 1939 self = null; 1940 assignedTo = TreeInfo.symbol(((JCAssign) frame.tree).getVariable()); 1941 break; 1942 default: 1943 assignedTo = self = null; 1944 break; 1945 } 1946 1947 // This symbol will be filled-in in complete 1948 if (owner.kind == MTH) { 1949 final MethodSymbol originalOwner = (MethodSymbol)owner.clone(owner.owner); 1950 this.translatedSym = new MethodSymbol(SYNTHETIC | PRIVATE, null, null, owner.enclClass()) { 1951 @Override 1952 public MethodSymbol originalEnclosingMethod() { 1953 return originalOwner; 1954 } 1955 }; 1956 } else { 1957 this.translatedSym = makePrivateSyntheticMethod(0, null, null, owner.enclClass()); 1958 } 1959 translatedSymbols = new EnumMap<>(LambdaSymbolKind.class); 1960 1961 translatedSymbols.put(PARAM, new LinkedHashMap<Symbol, Symbol>()); 1962 translatedSymbols.put(LOCAL_VAR, new LinkedHashMap<Symbol, Symbol>()); 1963 translatedSymbols.put(CAPTURED_VAR, new LinkedHashMap<Symbol, Symbol>()); 1964 translatedSymbols.put(CAPTURED_THIS, new LinkedHashMap<Symbol, Symbol>()); 1965 translatedSymbols.put(CAPTURED_OUTER_THIS, new LinkedHashMap<Symbol, Symbol>()); 1966 1967 freeVarProcessedLocalClasses = new HashSet<>(); 1968 } 1969 1970 /** 1971 * For a serializable lambda, generate a disambiguating string 1972 * which maximizes stability across deserialization. 1973 * 1974 * @return String to differentiate synthetic lambda method names 1975 */ 1976 private String serializedLambdaDisambiguation() { 1977 StringBuilder buf = new StringBuilder(); 1978 // Append the enclosing method signature to differentiate 1979 // overloaded enclosing methods. For lambdas enclosed in 1980 // lambdas, the generated lambda method will not have type yet, 1981 // but the enclosing method's name will have been generated 1982 // with this same method, so it will be unique and never be 1983 // overloaded. 1984 Assert.check( 1985 owner.type != null || 1986 directlyEnclosingLambda() != null); 1987 if (owner.type != null) { 1988 buf.append(typeSig(owner.type, true)); 1989 buf.append(":"); 1990 } 1991 1992 // Add target type info 1993 buf.append(types.findDescriptorSymbol(tree.type.tsym).owner.flatName()); 1994 buf.append(" "); 1995 1996 // Add variable assigned to 1997 if (assignedTo != null) { 1998 buf.append(assignedTo.flatName()); 1999 buf.append("="); 2000 } 2001 //add captured locals info: type, name, order 2002 for (Symbol fv : getSymbolMap(CAPTURED_VAR).keySet()) { 2003 if (fv != self) { 2004 buf.append(typeSig(fv.type, true)); 2005 buf.append(" "); 2006 buf.append(fv.flatName()); 2007 buf.append(","); 2008 } 2009 } 2010 2011 return buf.toString(); 2012 } 2013 2014 /** 2015 * For a non-serializable lambda, generate a simple method. 2016 * 2017 * @return Name to use for the synthetic lambda method name 2018 */ 2019 private Name lambdaName() { 2020 return names.lambda.append(names.fromString(enclosingMethodName() + "$" + lambdaCount++)); 2021 } 2022 2023 /** 2024 * For a serializable lambda, generate a method name which maximizes 2025 * name stability across deserialization. 2026 * 2027 * @return Name to use for the synthetic lambda method name 2028 */ 2029 private Name serializedLambdaName() { 2030 StringBuilder buf = new StringBuilder(); 2031 buf.append(names.lambda); 2032 // Append the name of the method enclosing the lambda. 2033 buf.append(enclosingMethodName()); 2034 buf.append('$'); 2035 // Append a hash of the disambiguating string : enclosing method 2036 // signature, etc. 2037 String disam = serializedLambdaDisambiguation(); 2038 buf.append(Integer.toHexString(disam.hashCode())); 2039 buf.append('$'); 2040 // The above appended name components may not be unique, append 2041 // a count based on the above name components. 2042 buf.append(syntheticMethodNameCounts.getIndex(buf)); 2043 String result = buf.toString(); 2044 //System.err.printf("serializedLambdaName: %s -- %s\n", result, disam); 2045 return names.fromString(result); 2046 } 2047 2048 /** 2049 * Translate a symbol of a given kind into something suitable for the 2050 * synthetic lambda body 2051 */ 2052 Symbol translate(final Symbol sym, LambdaSymbolKind skind) { 2053 Symbol ret; 2054 switch (skind) { 2055 case CAPTURED_THIS: 2056 ret = sym; // self represented 2057 break; 2058 case CAPTURED_VAR: 2059 ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, sym.name, types.erasure(sym.type), translatedSym) { 2060 @Override 2061 public Symbol baseSymbol() { 2062 //keep mapping with original captured symbol 2063 return sym; 2064 } 2065 }; 2066 break; 2067 case CAPTURED_OUTER_THIS: 2068 Name name = names.fromString(sym.flatName().toString().replace('.', '$') + names.dollarThis); 2069 ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym) { 2070 @Override 2071 public Symbol baseSymbol() { 2072 //keep mapping with original captured symbol 2073 return sym; 2074 } 2075 }; 2076 break; 2077 case LOCAL_VAR: 2078 ret = new VarSymbol(sym.flags() & FINAL, sym.name, sym.type, translatedSym) { 2079 @Override 2080 public Symbol baseSymbol() { 2081 //keep mapping with original symbol 2082 return sym; 2083 } 2084 }; 2085 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos; 2086 // If sym.data == ElementKind.EXCEPTION_PARAMETER, 2087 // set ret.data = ElementKind.EXCEPTION_PARAMETER too. 2088 // Because method com.sun.tools.javac.jvm.Code.fillExceptionParameterPositions and 2089 // com.sun.tools.javac.jvm.Code.fillLocalVarPosition would use it. 2090 // See JDK-8257740 for more information. 2091 if (((VarSymbol) sym).isExceptionParameter()) { 2092 ((VarSymbol) ret).setData(ElementKind.EXCEPTION_PARAMETER); 2093 } 2094 break; 2095 case PARAM: 2096 ret = new VarSymbol((sym.flags() & FINAL) | PARAMETER, sym.name, types.erasure(sym.type), translatedSym); 2097 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos; 2098 // Set ret.data. Same as case LOCAL_VAR above. 2099 if (((VarSymbol) sym).isExceptionParameter()) { 2100 ((VarSymbol) ret).setData(ElementKind.EXCEPTION_PARAMETER); 2101 } 2102 break; 2103 default: 2104 Assert.error(skind.name()); 2105 throw new AssertionError(); 2106 } 2107 if (ret != sym && skind.propagateAnnotations()) { 2108 ret.setDeclarationAttributes(sym.getRawAttributes()); 2109 ret.setTypeAttributes(sym.getRawTypeAttributes()); 2110 } 2111 return ret; 2112 } 2113 2114 void addSymbol(Symbol sym, LambdaSymbolKind skind) { 2115 if (skind == CAPTURED_THIS && sym != null && sym.kind == TYP && !typesUnderConstruction.isEmpty()) { 2116 ClassSymbol currentClass = currentClass(); 2117 if (currentClass != null && typesUnderConstruction.contains(currentClass)) { 2118 // reference must be to enclosing outer instance, mutate capture kind. 2119 Assert.check(sym != currentClass); // should have been caught right in Attr 2120 skind = CAPTURED_OUTER_THIS; 2121 } 2122 } 2123 Map<Symbol, Symbol> transMap = getSymbolMap(skind); 2124 if (!transMap.containsKey(sym)) { 2125 transMap.put(sym, translate(sym, skind)); 2126 } 2127 } 2128 2129 Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind skind) { 2130 Map<Symbol, Symbol> m = translatedSymbols.get(skind); 2131 Assert.checkNonNull(m); 2132 return m; 2133 } 2134 2135 JCTree translate(JCIdent lambdaIdent) { 2136 for (LambdaSymbolKind kind : LambdaSymbolKind.values()) { 2137 Map<Symbol, Symbol> m = getSymbolMap(kind); 2138 switch(kind) { 2139 default: 2140 if (m.containsKey(lambdaIdent.sym)) { 2141 Symbol tSym = m.get(lambdaIdent.sym); 2142 JCTree t = make.Ident(tSym).setType(lambdaIdent.type); 2143 return t; 2144 } 2145 break; 2146 case CAPTURED_OUTER_THIS: 2147 Optional<Symbol> proxy = m.keySet().stream() 2148 .filter(out -> lambdaIdent.sym.isMemberOf(out.type.tsym, types)) 2149 .reduce((a, b) -> a.isEnclosedBy((ClassSymbol)b) ? a : b); 2150 if (proxy.isPresent()) { 2151 // Transform outer instance variable references anchoring them to the captured synthetic. 2152 Symbol tSym = m.get(proxy.get()); 2153 JCExpression t = make.Ident(tSym).setType(lambdaIdent.sym.owner.type); 2154 t = make.Select(t, lambdaIdent.name); 2155 t.setType(lambdaIdent.type); 2156 TreeInfo.setSymbol(t, lambdaIdent.sym); 2157 return t; 2158 } 2159 break; 2160 } 2161 } 2162 return null; 2163 } 2164 2165 /* Translate away qualified this expressions, anchoring them to synthetic parameters that 2166 capture the qualified this handle. `fieldAccess' is guaranteed to one such. 2167 */ 2168 public JCTree translate(JCFieldAccess fieldAccess) { 2169 Assert.check(fieldAccess.name == names._this); 2170 Map<Symbol, Symbol> m = translatedSymbols.get(LambdaSymbolKind.CAPTURED_OUTER_THIS); 2171 if (m.containsKey(fieldAccess.sym.owner)) { 2172 Symbol tSym = m.get(fieldAccess.sym.owner); 2173 JCExpression t = make.Ident(tSym).setType(fieldAccess.sym.owner.type); 2174 return t; 2175 } 2176 return null; 2177 } 2178 2179 /* Translate away naked new instance creation expressions with implicit enclosing instances, 2180 anchoring them to synthetic parameters that stand proxy for the qualified outer this handle. 2181 */ 2182 public JCNewClass translate(JCNewClass newClass) { 2183 Assert.check(newClass.clazz.type.tsym.hasOuterInstance() && newClass.encl == null); 2184 Map<Symbol, Symbol> m = translatedSymbols.get(LambdaSymbolKind.CAPTURED_OUTER_THIS); 2185 final Type enclosingType = newClass.clazz.type.getEnclosingType(); 2186 if (m.containsKey(enclosingType.tsym)) { 2187 Symbol tSym = m.get(enclosingType.tsym); 2188 JCExpression encl = make.Ident(tSym).setType(enclosingType); 2189 newClass.encl = encl; 2190 } 2191 return newClass; 2192 } 2193 2194 /** 2195 * The translatedSym is not complete/accurate until the analysis is 2196 * finished. Once the analysis is finished, the translatedSym is 2197 * "completed" -- updated with type information, access modifiers, 2198 * and full parameter list. 2199 */ 2200 void complete() { 2201 if (syntheticParams != null) { 2202 return; 2203 } 2204 boolean inInterface = translatedSym.owner.isInterface(); 2205 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty(); 2206 2207 // If instance access isn't needed, make it static. 2208 // Interface instance methods must be default methods. 2209 // Lambda methods are private synthetic. 2210 // Inherit ACC_STRICT from the enclosing method, or, for clinit, 2211 // from the class. 2212 translatedSym.flags_field = SYNTHETIC | LAMBDA_METHOD | 2213 owner.flags_field & STRICTFP | 2214 owner.owner.flags_field & STRICTFP | 2215 PRIVATE | 2216 (thisReferenced? (inInterface? DEFAULT : 0) : STATIC); 2217 2218 //compute synthetic params 2219 ListBuffer<JCVariableDecl> params = new ListBuffer<>(); 2220 ListBuffer<VarSymbol> parameterSymbols = new ListBuffer<>(); 2221 2222 // The signature of the method is augmented with the following 2223 // synthetic parameters: 2224 // 2225 // 1) reference to enclosing contexts captured by the lambda expression 2226 // 2) enclosing locals captured by the lambda expression 2227 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR).values()) { 2228 params.append(make.VarDef((VarSymbol) thisSym, null)); 2229 parameterSymbols.append((VarSymbol) thisSym); 2230 } 2231 for (Symbol thisSym : getSymbolMap(CAPTURED_OUTER_THIS).values()) { 2232 params.append(make.VarDef((VarSymbol) thisSym, null)); 2233 parameterSymbols.append((VarSymbol) thisSym); 2234 } 2235 for (Symbol thisSym : getSymbolMap(PARAM).values()) { 2236 params.append(make.VarDef((VarSymbol) thisSym, null)); 2237 parameterSymbols.append((VarSymbol) thisSym); 2238 } 2239 syntheticParams = params.toList(); 2240 2241 translatedSym.params = parameterSymbols.toList(); 2242 2243 // Compute and set the lambda name 2244 translatedSym.name = isSerializable() 2245 ? serializedLambdaName() 2246 : lambdaName(); 2247 2248 //prepend synthetic args to translated lambda method signature 2249 translatedSym.type = types.createMethodTypeWithParameters( 2250 generatedLambdaSig(), 2251 TreeInfo.types(syntheticParams)); 2252 } 2253 2254 Type generatedLambdaSig() { 2255 return types.erasure(tree.getDescriptorType(types)); 2256 } 2257 } 2258 2259 /** 2260 * This class retains all the useful information about a method reference; 2261 * the contents of this class are filled by the LambdaAnalyzer visitor, 2262 * and the used by the main translation routines in order to adjust method 2263 * references (i.e. in case a bridge is needed) 2264 */ 2265 final class ReferenceTranslationContext extends TranslationContext<JCMemberReference> { 2266 2267 final boolean isSuper; 2268 2269 ReferenceTranslationContext(JCMemberReference tree) { 2270 super(tree); 2271 this.isSuper = tree.hasKind(ReferenceKind.SUPER); 2272 } 2273 2274 boolean needsVarArgsConversion() { 2275 return tree.varargsElement != null; 2276 } 2277 2278 /** 2279 * @return Is this an array operation like clone() 2280 */ 2281 boolean isArrayOp() { 2282 return tree.sym.owner == syms.arrayClass; 2283 } 2284 2285 boolean receiverAccessible() { 2286 //hack needed to workaround 292 bug (7087658) 2287 //when 292 issue is fixed we should remove this and change the backend 2288 //code to always generate a method handle to an accessible method 2289 return tree.ownerAccessible; 2290 } 2291 2292 /** 2293 * This method should be called only when target release <= 14 2294 * where LambdaMetaFactory does not spin nestmate classes. 2295 * 2296 * This method should be removed when --release 14 is not supported. 2297 */ 2298 boolean isPrivateInOtherClass() { 2299 assert !nestmateLambdas; 2300 return (tree.sym.flags() & PRIVATE) != 0 && 2301 !types.isSameType( 2302 types.erasure(tree.sym.enclClass().asType()), 2303 types.erasure(owner.enclClass().asType())); 2304 } 2305 2306 /** 2307 * Erasure destroys the implementation parameter subtype 2308 * relationship for intersection types. 2309 * Have similar problems for union types too. 2310 */ 2311 boolean interfaceParameterIsIntersectionOrUnionType() { 2312 List<Type> tl = tree.getDescriptorType(types).getParameterTypes(); 2313 for (; tl.nonEmpty(); tl = tl.tail) { 2314 Type pt = tl.head; 2315 if (isIntersectionOrUnionType(pt)) 2316 return true; 2317 } 2318 return false; 2319 } 2320 2321 boolean isIntersectionOrUnionType(Type t) { 2322 switch (t.getKind()) { 2323 case INTERSECTION: 2324 case UNION: 2325 return true; 2326 case TYPEVAR: 2327 TypeVar tv = (TypeVar) t; 2328 return isIntersectionOrUnionType(tv.getUpperBound()); 2329 } 2330 return false; 2331 } 2332 2333 /** 2334 * Does this reference need to be converted to a lambda 2335 * (i.e. var args need to be expanded or "super" is used) 2336 */ 2337 final boolean needsConversionToLambda() { 2338 return interfaceParameterIsIntersectionOrUnionType() || 2339 isSuper || 2340 needsVarArgsConversion() || 2341 isArrayOp() || 2342 (!nestmateLambdas && isPrivateInOtherClass()) || 2343 isProtectedInSuperClassOfEnclosingClassInOtherPackage(tree.sym, owner) || 2344 !receiverAccessible() || 2345 (tree.getMode() == ReferenceMode.NEW && 2346 tree.kind != ReferenceKind.ARRAY_CTOR && 2347 (tree.sym.owner.isDirectlyOrIndirectlyLocal() || tree.sym.owner.isInner())); 2348 } 2349 2350 Type generatedRefSig() { 2351 return types.erasure(tree.sym.type); 2352 } 2353 2354 Type bridgedRefSig() { 2355 return types.erasure(types.findDescriptorSymbol(tree.target.tsym).type); 2356 } 2357 } 2358 } 2359 // </editor-fold> 2360 2361 /* 2362 * These keys provide mappings for various translated lambda symbols 2363 * and the prevailing order must be maintained. 2364 */ 2365 enum LambdaSymbolKind { 2366 PARAM, // original to translated lambda parameters 2367 LOCAL_VAR, // original to translated lambda locals 2368 CAPTURED_VAR, // variables in enclosing scope to translated synthetic parameters 2369 CAPTURED_THIS, // class symbols to translated synthetic parameters (for captured member access) 2370 CAPTURED_OUTER_THIS; // used when `this' capture is illegal, but outer this capture is legit (JDK-8129740) 2371 2372 boolean propagateAnnotations() { 2373 switch (this) { 2374 case CAPTURED_VAR: 2375 case CAPTURED_THIS: 2376 case CAPTURED_OUTER_THIS: 2377 return false; 2378 default: 2379 return true; 2380 } 2381 } 2382 } 2383 2384 /** 2385 * **************************************************************** 2386 * Signature Generation 2387 * **************************************************************** 2388 */ 2389 2390 private String typeSig(Type type) { 2391 return typeSig(type, false); 2392 } 2393 2394 private String typeSig(Type type, boolean allowIllegalSignature) { 2395 try { 2396 L2MSignatureGenerator sg = new L2MSignatureGenerator(allowIllegalSignature); 2397 sg.assembleSig(type); 2398 return sg.toString(); 2399 } catch (InvalidSignatureException ex) { 2400 Symbol c = attrEnv.enclClass.sym; 2401 log.error(Errors.CannotGenerateClass(c, Fragments.IllegalSignature(c, ex.type()))); 2402 return "<ERRONEOUS>"; 2403 } 2404 } 2405 2406 private String classSig(Type type) { 2407 try { 2408 L2MSignatureGenerator sg = new L2MSignatureGenerator(false); 2409 sg.assembleClassSig(type); 2410 return sg.toString(); 2411 } catch (InvalidSignatureException ex) { 2412 Symbol c = attrEnv.enclClass.sym; 2413 log.error(Errors.CannotGenerateClass(c, Fragments.IllegalSignature(c, ex.type()))); 2414 return "<ERRONEOUS>"; 2415 } 2416 } 2417 2418 private boolean isProtectedInSuperClassOfEnclosingClassInOtherPackage(Symbol targetReference, 2419 Symbol currentClass) { 2420 return ((targetReference.flags() & PROTECTED) != 0 && 2421 targetReference.packge() != currentClass.packge()); 2422 } 2423 2424 /** 2425 * Signature Generation 2426 */ 2427 private class L2MSignatureGenerator extends Types.SignatureGenerator { 2428 2429 /** 2430 * An output buffer for type signatures. 2431 */ 2432 StringBuilder sb = new StringBuilder(); 2433 2434 /** 2435 * Are signatures incompatible with JVM spec allowed? 2436 * Used by {@link LambdaTranslationContext#serializedLambdaDisambiguation()}. 2437 */ 2438 boolean allowIllegalSignatures; 2439 2440 L2MSignatureGenerator(boolean allowIllegalSignatures) { 2441 super(types); 2442 this.allowIllegalSignatures = allowIllegalSignatures; 2443 } 2444 2445 @Override 2446 protected void reportIllegalSignature(Type t) { 2447 if (!allowIllegalSignatures) { 2448 super.reportIllegalSignature(t); 2449 } 2450 } 2451 2452 @Override 2453 protected void append(char ch) { 2454 sb.append(ch); 2455 } 2456 2457 @Override 2458 protected void append(byte[] ba) { 2459 Name name = names.fromUtf(ba); 2460 sb.append(name.toString()); 2461 } 2462 2463 @Override 2464 protected void append(Name name) { 2465 sb.append(name.toString()); 2466 } 2467 2468 @Override 2469 public String toString() { 2470 return sb.toString(); 2471 } 2472 } 2473 }