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