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