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