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