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