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