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