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