LambdaToMethod.java revision 3885:6c729485e202
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 case TYPECAST: setVarargsIfNeeded(((JCTypeCast) tree).expr, varargsElement); break; 796 default: throw new AssertionError(); 797 } 798 } 799 } 800 801 /** 802 * Convert method/constructor arguments by inserting appropriate cast 803 * as required by type-erasure - this is needed when bridging a lambda/method 804 * reference, as the bridged signature might require downcast to be compatible 805 * with the generated signature. 806 */ 807 private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) { 808 Assert.check(meth.kind == MTH); 809 List<Type> formals = types.erasure(meth.type).getParameterTypes(); 810 if (varargsElement != null) { 811 Assert.check((meth.flags() & VARARGS) != 0); 812 } 813 return transTypes.translateArgs(args, formals, varargsElement, attrEnv); 814 } 815 816 // </editor-fold> 817 818 /** 819 * Converts a method reference which cannot be used directly into a lambda 820 */ 821 private class MemberReferenceToLambda { 822 823 private final JCMemberReference tree; 824 private final ReferenceTranslationContext localContext; 825 private final Symbol owner; 826 private final ListBuffer<JCExpression> args = new ListBuffer<>(); 827 private final ListBuffer<JCVariableDecl> params = new ListBuffer<>(); 828 829 private JCExpression receiverExpression = null; 830 831 MemberReferenceToLambda(JCMemberReference tree, ReferenceTranslationContext localContext, Symbol owner) { 832 this.tree = tree; 833 this.localContext = localContext; 834 this.owner = owner; 835 } 836 837 JCLambda lambda() { 838 int prevPos = make.pos; 839 try { 840 make.at(tree); 841 842 //body generation - this can be either a method call or a 843 //new instance creation expression, depending on the member reference kind 844 VarSymbol rcvr = addParametersReturnReceiver(); 845 JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE) 846 ? expressionInvoke(rcvr) 847 : expressionNew(); 848 849 JCLambda slam = make.Lambda(params.toList(), expr); 850 slam.targets = tree.targets; 851 slam.type = tree.type; 852 slam.pos = tree.pos; 853 return slam; 854 } finally { 855 make.at(prevPos); 856 } 857 } 858 859 /** 860 * Generate the parameter list for the converted member reference. 861 * 862 * @return The receiver variable symbol, if any 863 */ 864 VarSymbol addParametersReturnReceiver() { 865 Type samDesc = localContext.bridgedRefSig(); 866 List<Type> samPTypes = samDesc.getParameterTypes(); 867 List<Type> descPTypes = tree.getDescriptorType(types).getParameterTypes(); 868 869 // Determine the receiver, if any 870 VarSymbol rcvr; 871 switch (tree.kind) { 872 case BOUND: 873 // The receiver is explicit in the method reference 874 rcvr = addParameter("rec$", tree.getQualifierExpression().type, false); 875 receiverExpression = attr.makeNullCheck(tree.getQualifierExpression()); 876 break; 877 case UNBOUND: 878 // The receiver is the first parameter, extract it and 879 // adjust the SAM and unerased type lists accordingly 880 rcvr = addParameter("rec$", samDesc.getParameterTypes().head, false); 881 samPTypes = samPTypes.tail; 882 descPTypes = descPTypes.tail; 883 break; 884 default: 885 rcvr = null; 886 break; 887 } 888 List<Type> implPTypes = tree.sym.type.getParameterTypes(); 889 int implSize = implPTypes.size(); 890 int samSize = samPTypes.size(); 891 // Last parameter to copy from referenced method, exclude final var args 892 int last = localContext.needsVarArgsConversion() ? implSize - 1 : implSize; 893 894 // Failsafe -- assure match-up 895 boolean checkForIntersection = tree.varargsElement != null || implSize == descPTypes.size(); 896 897 // Use parameter types of the implementation method unless the unerased 898 // SAM parameter type is an intersection type, in that case use the 899 // erased SAM parameter type so that the supertype relationship 900 // the implementation method parameters is not obscured. 901 // Note: in this loop, the lists implPTypes, samPTypes, and descPTypes 902 // are used as pointers to the current parameter type information 903 // and are thus not usable afterwards. 904 for (int i = 0; implPTypes.nonEmpty() && i < last; ++i) { 905 // By default use the implementation method parmeter type 906 Type parmType = implPTypes.head; 907 // If the unerased parameter type is a type variable whose 908 // bound is an intersection (eg. <T extends A & B>) then 909 // use the SAM parameter type 910 if (checkForIntersection && descPTypes.head.getKind() == TypeKind.TYPEVAR) { 911 TypeVar tv = (TypeVar) descPTypes.head; 912 if (tv.bound.getKind() == TypeKind.INTERSECTION) { 913 parmType = samPTypes.head; 914 } 915 } 916 addParameter("x$" + i, parmType, true); 917 918 // Advance to the next parameter 919 implPTypes = implPTypes.tail; 920 samPTypes = samPTypes.tail; 921 descPTypes = descPTypes.tail; 922 } 923 // Flatten out the var args 924 for (int i = last; i < samSize; ++i) { 925 addParameter("xva$" + i, tree.varargsElement, true); 926 } 927 928 return rcvr; 929 } 930 931 JCExpression getReceiverExpression() { 932 return receiverExpression; 933 } 934 935 private JCExpression makeReceiver(VarSymbol rcvr) { 936 if (rcvr == null) return null; 937 JCExpression rcvrExpr = make.Ident(rcvr); 938 Type rcvrType = tree.ownerAccessible ? tree.sym.enclClass().type : tree.expr.type; 939 if (rcvrType == syms.arrayClass.type) { 940 // Map the receiver type to the actually type, not just "array" 941 rcvrType = tree.getQualifierExpression().type; 942 } 943 if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) { 944 rcvrExpr = make.TypeCast(make.Type(rcvrType), rcvrExpr).setType(rcvrType); 945 } 946 return rcvrExpr; 947 } 948 949 /** 950 * determine the receiver of the method call - the receiver can 951 * be a type qualifier, the synthetic receiver parameter or 'super'. 952 */ 953 private JCExpression expressionInvoke(VarSymbol rcvr) { 954 JCExpression qualifier = 955 (rcvr != null) ? 956 makeReceiver(rcvr) : 957 tree.getQualifierExpression(); 958 959 //create the qualifier expression 960 JCFieldAccess select = make.Select(qualifier, tree.sym.name); 961 select.sym = tree.sym; 962 select.type = tree.sym.erasure(types); 963 964 //create the method call expression 965 JCExpression apply = make.Apply(List.nil(), select, 966 convertArgs(tree.sym, args.toList(), tree.varargsElement)). 967 setType(tree.sym.erasure(types).getReturnType()); 968 969 apply = transTypes.coerce(attrEnv, apply, 970 types.erasure(localContext.tree.referentType.getReturnType())); 971 972 setVarargsIfNeeded(apply, tree.varargsElement); 973 return apply; 974 } 975 976 /** 977 * Lambda body to use for a 'new'. 978 */ 979 private JCExpression expressionNew() { 980 if (tree.kind == ReferenceKind.ARRAY_CTOR) { 981 //create the array creation expression 982 JCNewArray newArr = make.NewArray( 983 make.Type(types.elemtype(tree.getQualifierExpression().type)), 984 List.of(make.Ident(params.first())), 985 null); 986 newArr.type = tree.getQualifierExpression().type; 987 return newArr; 988 } else { 989 //create the instance creation expression 990 //note that method reference syntax does not allow an explicit 991 //enclosing class (so the enclosing class is null) 992 JCNewClass newClass = make.NewClass(null, 993 List.nil(), 994 make.Type(tree.getQualifierExpression().type), 995 convertArgs(tree.sym, args.toList(), tree.varargsElement), 996 null); 997 newClass.constructor = tree.sym; 998 newClass.constructorType = tree.sym.erasure(types); 999 newClass.type = tree.getQualifierExpression().type; 1000 setVarargsIfNeeded(newClass, tree.varargsElement); 1001 return newClass; 1002 } 1003 } 1004 1005 private VarSymbol addParameter(String name, Type p, boolean genArg) { 1006 VarSymbol vsym = new VarSymbol(PARAMETER | SYNTHETIC, names.fromString(name), p, owner); 1007 vsym.pos = tree.pos; 1008 params.append(make.VarDef(vsym, null)); 1009 if (genArg) { 1010 args.append(make.Ident(vsym)); 1011 } 1012 return vsym; 1013 } 1014 } 1015 1016 private MethodType typeToMethodType(Type mt) { 1017 Type type = types.erasure(mt); 1018 return new MethodType(type.getParameterTypes(), 1019 type.getReturnType(), 1020 type.getThrownTypes(), 1021 syms.methodClass); 1022 } 1023 1024 /** 1025 * Generate an indy method call to the meta factory 1026 */ 1027 private JCExpression makeMetafactoryIndyCall(TranslationContext<?> context, 1028 int refKind, Symbol refSym, List<JCExpression> indy_args) { 1029 JCFunctionalExpression tree = context.tree; 1030 //determine the static bsm args 1031 MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym); 1032 List<Object> staticArgs = List.of( 1033 typeToMethodType(samSym.type), 1034 new Pool.MethodHandle(refKind, refSym, types), 1035 typeToMethodType(tree.getDescriptorType(types))); 1036 1037 //computed indy arg types 1038 ListBuffer<Type> indy_args_types = new ListBuffer<>(); 1039 for (JCExpression arg : indy_args) { 1040 indy_args_types.append(arg.type); 1041 } 1042 1043 //finally, compute the type of the indy call 1044 MethodType indyType = new MethodType(indy_args_types.toList(), 1045 tree.type, 1046 List.nil(), 1047 syms.methodClass); 1048 1049 Name metafactoryName = context.needsAltMetafactory() ? 1050 names.altMetafactory : names.metafactory; 1051 1052 if (context.needsAltMetafactory()) { 1053 ListBuffer<Object> markers = new ListBuffer<>(); 1054 for (Type t : tree.targets.tail) { 1055 if (t.tsym != syms.serializableType.tsym) { 1056 markers.append(t.tsym); 1057 } 1058 } 1059 int flags = context.isSerializable() ? FLAG_SERIALIZABLE : 0; 1060 boolean hasMarkers = markers.nonEmpty(); 1061 boolean hasBridges = context.bridges.nonEmpty(); 1062 if (hasMarkers) { 1063 flags |= FLAG_MARKERS; 1064 } 1065 if (hasBridges) { 1066 flags |= FLAG_BRIDGES; 1067 } 1068 staticArgs = staticArgs.append(flags); 1069 if (hasMarkers) { 1070 staticArgs = staticArgs.append(markers.length()); 1071 staticArgs = staticArgs.appendList(markers.toList()); 1072 } 1073 if (hasBridges) { 1074 staticArgs = staticArgs.append(context.bridges.length() - 1); 1075 for (Symbol s : context.bridges) { 1076 Type s_erasure = s.erasure(types); 1077 if (!types.isSameType(s_erasure, samSym.erasure(types))) { 1078 staticArgs = staticArgs.append(s.erasure(types)); 1079 } 1080 } 1081 } 1082 if (context.isSerializable()) { 1083 int prevPos = make.pos; 1084 try { 1085 make.at(kInfo.clazz); 1086 addDeserializationCase(refKind, refSym, tree.type, samSym, 1087 tree, staticArgs, indyType); 1088 } finally { 1089 make.at(prevPos); 1090 } 1091 } 1092 } 1093 1094 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args, samSym.name); 1095 } 1096 1097 /** 1098 * Generate an indy method call with given name, type and static bootstrap 1099 * arguments types 1100 */ 1101 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName, 1102 List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs, 1103 Name methName) { 1104 int prevPos = make.pos; 1105 try { 1106 make.at(pos); 1107 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType, 1108 syms.stringType, 1109 syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs)); 1110 1111 Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site, 1112 bsmName, bsm_staticArgs, List.nil()); 1113 1114 DynamicMethodSymbol dynSym = 1115 new DynamicMethodSymbol(methName, 1116 syms.noSymbol, 1117 bsm.isStatic() ? 1118 ClassFile.REF_invokeStatic : 1119 ClassFile.REF_invokeVirtual, 1120 (MethodSymbol)bsm, 1121 indyType, 1122 staticArgs.toArray()); 1123 1124 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName); 1125 qualifier.sym = dynSym; 1126 qualifier.type = indyType.getReturnType(); 1127 1128 JCMethodInvocation proxyCall = make.Apply(List.nil(), qualifier, indyArgs); 1129 proxyCall.type = indyType.getReturnType(); 1130 return proxyCall; 1131 } finally { 1132 make.at(prevPos); 1133 } 1134 } 1135 //where 1136 private List<Type> bsmStaticArgToTypes(List<Object> args) { 1137 ListBuffer<Type> argtypes = new ListBuffer<>(); 1138 for (Object arg : args) { 1139 argtypes.append(bsmStaticArgToType(arg)); 1140 } 1141 return argtypes.toList(); 1142 } 1143 1144 private Type bsmStaticArgToType(Object arg) { 1145 Assert.checkNonNull(arg); 1146 if (arg instanceof ClassSymbol) { 1147 return syms.classType; 1148 } else if (arg instanceof Integer) { 1149 return syms.intType; 1150 } else if (arg instanceof Long) { 1151 return syms.longType; 1152 } else if (arg instanceof Float) { 1153 return syms.floatType; 1154 } else if (arg instanceof Double) { 1155 return syms.doubleType; 1156 } else if (arg instanceof String) { 1157 return syms.stringType; 1158 } else if (arg instanceof Pool.MethodHandle) { 1159 return syms.methodHandleType; 1160 } else if (arg instanceof MethodType) { 1161 return syms.methodTypeType; 1162 } else { 1163 Assert.error("bad static arg " + arg.getClass()); 1164 return null; 1165 } 1166 } 1167 1168 /** 1169 * Get the opcode associated with this method reference 1170 */ 1171 private int referenceKind(Symbol refSym) { 1172 if (refSym.isConstructor()) { 1173 return ClassFile.REF_newInvokeSpecial; 1174 } else { 1175 if (refSym.isStatic()) { 1176 return ClassFile.REF_invokeStatic; 1177 } else if ((refSym.flags() & PRIVATE) != 0) { 1178 return ClassFile.REF_invokeSpecial; 1179 } else if (refSym.enclClass().isInterface()) { 1180 return ClassFile.REF_invokeInterface; 1181 } else { 1182 return ClassFile.REF_invokeVirtual; 1183 } 1184 } 1185 } 1186 1187 // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer"> 1188 /** 1189 * This visitor collects information about translation of a lambda expression. 1190 * More specifically, it keeps track of the enclosing contexts and captured locals 1191 * accessed by the lambda being translated (as well as other useful info). 1192 * It also translates away problems for LambdaToMethod. 1193 */ 1194 class LambdaAnalyzerPreprocessor extends TreeTranslator { 1195 1196 /** the frame stack - used to reconstruct translation info about enclosing scopes */ 1197 private List<Frame> frameStack; 1198 1199 /** 1200 * keep the count of lambda expression (used to generate unambiguous 1201 * names) 1202 */ 1203 private int lambdaCount = 0; 1204 1205 /** 1206 * List of types undergoing construction via explicit constructor chaining. 1207 */ 1208 private List<ClassSymbol> typesUnderConstruction; 1209 1210 /** 1211 * keep the count of lambda expression defined in given context (used to 1212 * generate unambiguous names for serializable lambdas) 1213 */ 1214 private class SyntheticMethodNameCounter { 1215 private Map<String, Integer> map = new HashMap<>(); 1216 int getIndex(StringBuilder buf) { 1217 String temp = buf.toString(); 1218 Integer count = map.get(temp); 1219 if (count == null) { 1220 count = 0; 1221 } 1222 ++count; 1223 map.put(temp, count); 1224 return count; 1225 } 1226 } 1227 private SyntheticMethodNameCounter syntheticMethodNameCounts = 1228 new SyntheticMethodNameCounter(); 1229 1230 private Map<Symbol, JCClassDecl> localClassDefs; 1231 1232 /** 1233 * maps for fake clinit symbols to be used as owners of lambda occurring in 1234 * a static var init context 1235 */ 1236 private Map<ClassSymbol, Symbol> clinits = new HashMap<>(); 1237 1238 private JCClassDecl analyzeAndPreprocessClass(JCClassDecl tree) { 1239 frameStack = List.nil(); 1240 typesUnderConstruction = List.nil(); 1241 localClassDefs = new HashMap<>(); 1242 return translate(tree); 1243 } 1244 1245 @Override 1246 public void visitApply(JCMethodInvocation tree) { 1247 List<ClassSymbol> previousNascentTypes = typesUnderConstruction; 1248 try { 1249 Name methName = TreeInfo.name(tree.meth); 1250 if (methName == names._this || methName == names._super) { 1251 typesUnderConstruction = typesUnderConstruction.prepend(currentClass()); 1252 } 1253 super.visitApply(tree); 1254 } finally { 1255 typesUnderConstruction = previousNascentTypes; 1256 } 1257 } 1258 // where 1259 private ClassSymbol currentClass() { 1260 for (Frame frame : frameStack) { 1261 if (frame.tree.hasTag(JCTree.Tag.CLASSDEF)) { 1262 JCClassDecl cdef = (JCClassDecl) frame.tree; 1263 return cdef.sym; 1264 } 1265 } 1266 return null; 1267 } 1268 1269 @Override 1270 public void visitBlock(JCBlock tree) { 1271 List<Frame> prevStack = frameStack; 1272 try { 1273 if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) { 1274 frameStack = frameStack.prepend(new Frame(tree)); 1275 } 1276 super.visitBlock(tree); 1277 } 1278 finally { 1279 frameStack = prevStack; 1280 } 1281 } 1282 1283 @Override 1284 public void visitClassDef(JCClassDecl tree) { 1285 List<Frame> prevStack = frameStack; 1286 int prevLambdaCount = lambdaCount; 1287 SyntheticMethodNameCounter prevSyntheticMethodNameCounts = 1288 syntheticMethodNameCounts; 1289 Map<ClassSymbol, Symbol> prevClinits = clinits; 1290 DiagnosticSource prevSource = log.currentSource(); 1291 try { 1292 log.useSource(tree.sym.sourcefile); 1293 lambdaCount = 0; 1294 syntheticMethodNameCounts = new SyntheticMethodNameCounter(); 1295 prevClinits = new HashMap<>(); 1296 if (tree.sym.owner.kind == MTH) { 1297 localClassDefs.put(tree.sym, tree); 1298 } 1299 if (directlyEnclosingLambda() != null) { 1300 tree.sym.owner = owner(); 1301 if (tree.sym.hasOuterInstance()) { 1302 //if a class is defined within a lambda, the lambda must capture 1303 //its enclosing instance (if any) 1304 TranslationContext<?> localContext = context(); 1305 final TypeSymbol outerInstanceSymbol = tree.sym.type.getEnclosingType().tsym; 1306 while (localContext != null && !localContext.owner.isStatic()) { 1307 if (localContext.tree.hasTag(LAMBDA)) { 1308 JCTree block = capturedDecl(localContext.depth, outerInstanceSymbol); 1309 if (block == null) break; 1310 ((LambdaTranslationContext)localContext) 1311 .addSymbol(outerInstanceSymbol, CAPTURED_THIS); 1312 } 1313 localContext = localContext.prev; 1314 } 1315 } 1316 } 1317 frameStack = frameStack.prepend(new Frame(tree)); 1318 super.visitClassDef(tree); 1319 } 1320 finally { 1321 log.useSource(prevSource.getFile()); 1322 frameStack = prevStack; 1323 lambdaCount = prevLambdaCount; 1324 syntheticMethodNameCounts = prevSyntheticMethodNameCounts; 1325 clinits = prevClinits; 1326 } 1327 } 1328 1329 @Override 1330 public void visitIdent(JCIdent tree) { 1331 if (context() != null && lambdaIdentSymbolFilter(tree.sym)) { 1332 if (tree.sym.kind == VAR && 1333 tree.sym.owner.kind == MTH && 1334 tree.type.constValue() == null) { 1335 TranslationContext<?> localContext = context(); 1336 while (localContext != null) { 1337 if (localContext.tree.getTag() == LAMBDA) { 1338 JCTree block = capturedDecl(localContext.depth, tree.sym); 1339 if (block == null) break; 1340 ((LambdaTranslationContext)localContext) 1341 .addSymbol(tree.sym, CAPTURED_VAR); 1342 } 1343 localContext = localContext.prev; 1344 } 1345 } else if (tree.sym.owner.kind == TYP) { 1346 TranslationContext<?> localContext = context(); 1347 while (localContext != null && !localContext.owner.isStatic()) { 1348 if (localContext.tree.hasTag(LAMBDA)) { 1349 JCTree block = capturedDecl(localContext.depth, tree.sym); 1350 if (block == null) break; 1351 switch (block.getTag()) { 1352 case CLASSDEF: 1353 JCClassDecl cdecl = (JCClassDecl)block; 1354 ((LambdaTranslationContext)localContext) 1355 .addSymbol(cdecl.sym, CAPTURED_THIS); 1356 break; 1357 default: 1358 Assert.error("bad block kind"); 1359 } 1360 } 1361 localContext = localContext.prev; 1362 } 1363 } 1364 } 1365 super.visitIdent(tree); 1366 } 1367 1368 @Override 1369 public void visitLambda(JCLambda tree) { 1370 analyzeLambda(tree, "lambda.stat"); 1371 } 1372 1373 private void analyzeLambda(JCLambda tree, JCExpression methodReferenceReceiver) { 1374 // Translation of the receiver expression must occur first 1375 JCExpression rcvr = translate(methodReferenceReceiver); 1376 LambdaTranslationContext context = analyzeLambda(tree, "mref.stat.1"); 1377 if (rcvr != null) { 1378 context.methodReferenceReceiver = rcvr; 1379 } 1380 } 1381 1382 private LambdaTranslationContext analyzeLambda(JCLambda tree, String statKey) { 1383 List<Frame> prevStack = frameStack; 1384 try { 1385 LambdaTranslationContext context = new LambdaTranslationContext(tree); 1386 frameStack = frameStack.prepend(new Frame(tree)); 1387 for (JCVariableDecl param : tree.params) { 1388 context.addSymbol(param.sym, PARAM); 1389 frameStack.head.addLocal(param.sym); 1390 } 1391 contextMap.put(tree, context); 1392 super.visitLambda(tree); 1393 context.complete(); 1394 if (dumpLambdaToMethodStats) { 1395 log.note(tree, statKey, context.needsAltMetafactory(), context.translatedSym); 1396 } 1397 return context; 1398 } 1399 finally { 1400 frameStack = prevStack; 1401 } 1402 } 1403 1404 @Override 1405 public void visitMethodDef(JCMethodDecl tree) { 1406 List<Frame> prevStack = frameStack; 1407 try { 1408 frameStack = frameStack.prepend(new Frame(tree)); 1409 super.visitMethodDef(tree); 1410 } 1411 finally { 1412 frameStack = prevStack; 1413 } 1414 } 1415 1416 @Override 1417 public void visitNewClass(JCNewClass tree) { 1418 TypeSymbol def = tree.type.tsym; 1419 boolean inReferencedClass = currentlyInClass(def); 1420 boolean isLocal = def.isLocal(); 1421 if ((inReferencedClass && isLocal || lambdaNewClassFilter(context(), tree))) { 1422 TranslationContext<?> localContext = context(); 1423 final TypeSymbol outerInstanceSymbol = tree.type.getEnclosingType().tsym; 1424 while (localContext != null && !localContext.owner.isStatic()) { 1425 if (localContext.tree.hasTag(LAMBDA)) { 1426 if (outerInstanceSymbol != null) { 1427 JCTree block = capturedDecl(localContext.depth, outerInstanceSymbol); 1428 if (block == null) break; 1429 } 1430 ((LambdaTranslationContext)localContext) 1431 .addSymbol(outerInstanceSymbol, CAPTURED_THIS); 1432 } 1433 localContext = localContext.prev; 1434 } 1435 } 1436 if (context() != null && !inReferencedClass && isLocal) { 1437 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context(); 1438 captureLocalClassDefs(def, lambdaContext); 1439 } 1440 super.visitNewClass(tree); 1441 } 1442 //where 1443 void captureLocalClassDefs(Symbol csym, final LambdaTranslationContext lambdaContext) { 1444 JCClassDecl localCDef = localClassDefs.get(csym); 1445 if (localCDef != null && lambdaContext.freeVarProcessedLocalClasses.add(csym)) { 1446 BasicFreeVarCollector fvc = lower.new BasicFreeVarCollector() { 1447 @Override 1448 void addFreeVars(ClassSymbol c) { 1449 captureLocalClassDefs(c, lambdaContext); 1450 } 1451 @Override 1452 void visitSymbol(Symbol sym) { 1453 if (sym.kind == VAR && 1454 sym.owner.kind == MTH && 1455 ((VarSymbol)sym).getConstValue() == null) { 1456 TranslationContext<?> localContext = context(); 1457 while (localContext != null) { 1458 if (localContext.tree.getTag() == LAMBDA) { 1459 JCTree block = capturedDecl(localContext.depth, sym); 1460 if (block == null) break; 1461 ((LambdaTranslationContext)localContext).addSymbol(sym, CAPTURED_VAR); 1462 } 1463 localContext = localContext.prev; 1464 } 1465 } 1466 } 1467 }; 1468 fvc.scan(localCDef); 1469 } 1470 } 1471 //where 1472 boolean currentlyInClass(Symbol csym) { 1473 for (Frame frame : frameStack) { 1474 if (frame.tree.hasTag(JCTree.Tag.CLASSDEF)) { 1475 JCClassDecl cdef = (JCClassDecl) frame.tree; 1476 if (cdef.sym == csym) { 1477 return true; 1478 } 1479 } 1480 } 1481 return false; 1482 } 1483 1484 /** 1485 * Method references to local class constructors, may, if the local 1486 * class references local variables, have implicit constructor 1487 * parameters added in Lower; As a result, the invokedynamic bootstrap 1488 * information added in the LambdaToMethod pass will have the wrong 1489 * signature. Hooks between Lower and LambdaToMethod have been added to 1490 * handle normal "new" in this case. This visitor converts potentially 1491 * affected method references into a lambda containing a normal 1492 * expression. 1493 * 1494 * @param tree 1495 */ 1496 @Override 1497 public void visitReference(JCMemberReference tree) { 1498 ReferenceTranslationContext rcontext = new ReferenceTranslationContext(tree); 1499 contextMap.put(tree, rcontext); 1500 if (rcontext.needsConversionToLambda()) { 1501 // Convert to a lambda, and process as such 1502 MemberReferenceToLambda conv = new MemberReferenceToLambda(tree, rcontext, owner()); 1503 analyzeLambda(conv.lambda(), conv.getReceiverExpression()); 1504 } else { 1505 super.visitReference(tree); 1506 if (dumpLambdaToMethodStats) { 1507 log.note(tree, "mref.stat", rcontext.needsAltMetafactory(), null); 1508 } 1509 } 1510 } 1511 1512 @Override 1513 public void visitSelect(JCFieldAccess tree) { 1514 if (context() != null && tree.sym.kind == VAR && 1515 (tree.sym.name == names._this || 1516 tree.sym.name == names._super)) { 1517 // A select of this or super means, if we are in a lambda, 1518 // we much have an instance context 1519 TranslationContext<?> localContext = context(); 1520 while (localContext != null && !localContext.owner.isStatic()) { 1521 if (localContext.tree.hasTag(LAMBDA)) { 1522 JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym); 1523 if (clazz == null) break; 1524 ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS); 1525 } 1526 localContext = localContext.prev; 1527 } 1528 } 1529 super.visitSelect(tree); 1530 } 1531 1532 @Override 1533 public void visitVarDef(JCVariableDecl tree) { 1534 TranslationContext<?> context = context(); 1535 LambdaTranslationContext ltc = (context != null && context instanceof LambdaTranslationContext)? 1536 (LambdaTranslationContext)context : 1537 null; 1538 if (ltc != null) { 1539 if (frameStack.head.tree.hasTag(LAMBDA)) { 1540 ltc.addSymbol(tree.sym, LOCAL_VAR); 1541 } 1542 // Check for type variables (including as type arguments). 1543 // If they occur within class nested in a lambda, mark for erasure 1544 Type type = tree.sym.asType(); 1545 if (inClassWithinLambda() && !types.isSameType(types.erasure(type), type)) { 1546 ltc.addSymbol(tree.sym, TYPE_VAR); 1547 } 1548 } 1549 1550 List<Frame> prevStack = frameStack; 1551 try { 1552 if (tree.sym.owner.kind == MTH) { 1553 frameStack.head.addLocal(tree.sym); 1554 } 1555 frameStack = frameStack.prepend(new Frame(tree)); 1556 super.visitVarDef(tree); 1557 } 1558 finally { 1559 frameStack = prevStack; 1560 } 1561 } 1562 1563 /** 1564 * Return a valid owner given the current declaration stack 1565 * (required to skip synthetic lambda symbols) 1566 */ 1567 private Symbol owner() { 1568 return owner(false); 1569 } 1570 1571 @SuppressWarnings("fallthrough") 1572 private Symbol owner(boolean skipLambda) { 1573 List<Frame> frameStack2 = frameStack; 1574 while (frameStack2.nonEmpty()) { 1575 switch (frameStack2.head.tree.getTag()) { 1576 case VARDEF: 1577 if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) { 1578 frameStack2 = frameStack2.tail; 1579 break; 1580 } 1581 JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree; 1582 return initSym(cdecl.sym, 1583 ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC); 1584 case BLOCK: 1585 JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree; 1586 return initSym(cdecl2.sym, 1587 ((JCBlock)frameStack2.head.tree).flags & STATIC); 1588 case CLASSDEF: 1589 return ((JCClassDecl)frameStack2.head.tree).sym; 1590 case METHODDEF: 1591 return ((JCMethodDecl)frameStack2.head.tree).sym; 1592 case LAMBDA: 1593 if (!skipLambda) 1594 return ((LambdaTranslationContext)contextMap 1595 .get(frameStack2.head.tree)).translatedSym; 1596 default: 1597 frameStack2 = frameStack2.tail; 1598 } 1599 } 1600 Assert.error(); 1601 return null; 1602 } 1603 1604 private Symbol initSym(ClassSymbol csym, long flags) { 1605 boolean isStatic = (flags & STATIC) != 0; 1606 if (isStatic) { 1607 /* static clinits are generated in Gen, so we need to use a fake 1608 * one. Attr creates a fake clinit method while attributing 1609 * lambda expressions used as initializers of static fields, so 1610 * let's use that one. 1611 */ 1612 MethodSymbol clinit = attr.removeClinit(csym); 1613 if (clinit != null) { 1614 clinits.put(csym, clinit); 1615 return clinit; 1616 } 1617 1618 /* if no clinit is found at Attr, then let's try at clinits. 1619 */ 1620 clinit = (MethodSymbol)clinits.get(csym); 1621 if (clinit == null) { 1622 /* no luck, let's create a new one 1623 */ 1624 clinit = makePrivateSyntheticMethod(STATIC, 1625 names.clinit, 1626 new MethodType(List.nil(), syms.voidType, 1627 List.nil(), syms.methodClass), 1628 csym); 1629 clinits.put(csym, clinit); 1630 } 1631 return clinit; 1632 } else { 1633 //get the first constructor and treat it as the instance init sym 1634 for (Symbol s : csym.members_field.getSymbolsByName(names.init)) { 1635 return s; 1636 } 1637 } 1638 Assert.error("init not found"); 1639 return null; 1640 } 1641 1642 private JCTree directlyEnclosingLambda() { 1643 if (frameStack.isEmpty()) { 1644 return null; 1645 } 1646 List<Frame> frameStack2 = frameStack; 1647 while (frameStack2.nonEmpty()) { 1648 switch (frameStack2.head.tree.getTag()) { 1649 case CLASSDEF: 1650 case METHODDEF: 1651 return null; 1652 case LAMBDA: 1653 return frameStack2.head.tree; 1654 default: 1655 frameStack2 = frameStack2.tail; 1656 } 1657 } 1658 Assert.error(); 1659 return null; 1660 } 1661 1662 private boolean inClassWithinLambda() { 1663 if (frameStack.isEmpty()) { 1664 return false; 1665 } 1666 List<Frame> frameStack2 = frameStack; 1667 boolean classFound = false; 1668 while (frameStack2.nonEmpty()) { 1669 switch (frameStack2.head.tree.getTag()) { 1670 case LAMBDA: 1671 return classFound; 1672 case CLASSDEF: 1673 classFound = true; 1674 frameStack2 = frameStack2.tail; 1675 break; 1676 default: 1677 frameStack2 = frameStack2.tail; 1678 } 1679 } 1680 // No lambda 1681 return false; 1682 } 1683 1684 /** 1685 * Return the declaration corresponding to a symbol in the enclosing 1686 * scope; the depth parameter is used to filter out symbols defined 1687 * in nested scopes (which do not need to undergo capture). 1688 */ 1689 private JCTree capturedDecl(int depth, Symbol sym) { 1690 int currentDepth = frameStack.size() - 1; 1691 for (Frame block : frameStack) { 1692 switch (block.tree.getTag()) { 1693 case CLASSDEF: 1694 ClassSymbol clazz = ((JCClassDecl)block.tree).sym; 1695 if (clazz.isSubClass(sym, types) || sym.isMemberOf(clazz, types)) { 1696 return currentDepth > depth ? null : block.tree; 1697 } 1698 break; 1699 case VARDEF: 1700 if (((JCVariableDecl)block.tree).sym == sym && 1701 sym.owner.kind == MTH) { //only locals are captured 1702 return currentDepth > depth ? null : block.tree; 1703 } 1704 break; 1705 case BLOCK: 1706 case METHODDEF: 1707 case LAMBDA: 1708 if (block.locals != null && block.locals.contains(sym)) { 1709 return currentDepth > depth ? null : block.tree; 1710 } 1711 break; 1712 default: 1713 Assert.error("bad decl kind " + block.tree.getTag()); 1714 } 1715 currentDepth--; 1716 } 1717 return null; 1718 } 1719 1720 private TranslationContext<?> context() { 1721 for (Frame frame : frameStack) { 1722 TranslationContext<?> context = contextMap.get(frame.tree); 1723 if (context != null) { 1724 return context; 1725 } 1726 } 1727 return null; 1728 } 1729 1730 /** 1731 * This is used to filter out those identifiers that needs to be adjusted 1732 * when translating away lambda expressions 1733 */ 1734 private boolean lambdaIdentSymbolFilter(Symbol sym) { 1735 return (sym.kind == VAR || sym.kind == MTH) 1736 && !sym.isStatic() 1737 && sym.name != names.init; 1738 } 1739 1740 /** 1741 * This is used to filter out those select nodes that need to be adjusted 1742 * when translating away lambda expressions - at the moment, this is the 1743 * set of nodes that select `this' (qualified this) 1744 */ 1745 private boolean lambdaFieldAccessFilter(JCFieldAccess fAccess) { 1746 LambdaTranslationContext lambdaContext = 1747 context instanceof LambdaTranslationContext ? 1748 (LambdaTranslationContext) context : null; 1749 return lambdaContext != null 1750 && !fAccess.sym.isStatic() 1751 && fAccess.name == names._this 1752 && (fAccess.sym.owner.kind == TYP) 1753 && !lambdaContext.translatedSymbols.get(CAPTURED_OUTER_THIS).isEmpty(); 1754 } 1755 1756 /** 1757 * This is used to filter out those new class expressions that need to 1758 * be qualified with an enclosing tree 1759 */ 1760 private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) { 1761 if (context != null 1762 && tree.encl == null 1763 && tree.def == null 1764 && !tree.type.getEnclosingType().hasTag(NONE)) { 1765 Type encl = tree.type.getEnclosingType(); 1766 Type current = context.owner.enclClass().type; 1767 while (!current.hasTag(NONE)) { 1768 if (current.tsym.isSubClass(encl.tsym, types)) { 1769 return true; 1770 } 1771 current = current.getEnclosingType(); 1772 } 1773 return false; 1774 } else { 1775 return false; 1776 } 1777 } 1778 1779 private class Frame { 1780 final JCTree tree; 1781 List<Symbol> locals; 1782 1783 public Frame(JCTree tree) { 1784 this.tree = tree; 1785 } 1786 1787 void addLocal(Symbol sym) { 1788 if (locals == null) { 1789 locals = List.nil(); 1790 } 1791 locals = locals.prepend(sym); 1792 } 1793 } 1794 1795 /** 1796 * This class is used to store important information regarding translation of 1797 * lambda expression/method references (see subclasses). 1798 */ 1799 abstract class TranslationContext<T extends JCFunctionalExpression> { 1800 1801 /** the underlying (untranslated) tree */ 1802 final T tree; 1803 1804 /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */ 1805 final Symbol owner; 1806 1807 /** the depth of this lambda expression in the frame stack */ 1808 final int depth; 1809 1810 /** the enclosing translation context (set for nested lambdas/mref) */ 1811 final TranslationContext<?> prev; 1812 1813 /** list of methods to be bridged by the meta-factory */ 1814 final List<Symbol> bridges; 1815 1816 TranslationContext(T tree) { 1817 this.tree = tree; 1818 this.owner = owner(true); 1819 this.depth = frameStack.size() - 1; 1820 this.prev = context(); 1821 ClassSymbol csym = 1822 types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.targets, ABSTRACT | INTERFACE); 1823 this.bridges = types.functionalInterfaceBridges(csym); 1824 } 1825 1826 /** does this functional expression need to be created using alternate metafactory? */ 1827 boolean needsAltMetafactory() { 1828 return tree.targets.length() > 1 || 1829 isSerializable() || 1830 bridges.length() > 1; 1831 } 1832 1833 /** does this functional expression require serialization support? */ 1834 boolean isSerializable() { 1835 if (forceSerializable) { 1836 return true; 1837 } 1838 for (Type target : tree.targets) { 1839 if (types.asSuper(target, syms.serializableType.tsym) != null) { 1840 return true; 1841 } 1842 } 1843 return false; 1844 } 1845 1846 /** 1847 * @return Name of the enclosing method to be folded into synthetic 1848 * method name 1849 */ 1850 String enclosingMethodName() { 1851 return syntheticMethodNameComponent(owner.name); 1852 } 1853 1854 /** 1855 * @return Method name in a form that can be folded into a 1856 * component of a synthetic method name 1857 */ 1858 String syntheticMethodNameComponent(Name name) { 1859 if (name == null) { 1860 return "null"; 1861 } 1862 String methodName = name.toString(); 1863 if (methodName.equals("<clinit>")) { 1864 methodName = "static"; 1865 } else if (methodName.equals("<init>")) { 1866 methodName = "new"; 1867 } 1868 return methodName; 1869 } 1870 } 1871 1872 /** 1873 * This class retains all the useful information about a lambda expression; 1874 * the contents of this class are filled by the LambdaAnalyzer visitor, 1875 * and the used by the main translation routines in order to adjust references 1876 * to captured locals/members, etc. 1877 */ 1878 class LambdaTranslationContext extends TranslationContext<JCLambda> { 1879 1880 /** variable in the enclosing context to which this lambda is assigned */ 1881 final Symbol self; 1882 1883 /** variable in the enclosing context to which this lambda is assigned */ 1884 final Symbol assignedTo; 1885 1886 Map<LambdaSymbolKind, Map<Symbol, Symbol>> translatedSymbols; 1887 1888 /** the synthetic symbol for the method hoisting the translated lambda */ 1889 MethodSymbol translatedSym; 1890 1891 List<JCVariableDecl> syntheticParams; 1892 1893 /** 1894 * to prevent recursion, track local classes processed 1895 */ 1896 final Set<Symbol> freeVarProcessedLocalClasses; 1897 1898 /** 1899 * For method references converted to lambdas. The method 1900 * reference receiver expression. Must be treated like a captured 1901 * variable. 1902 */ 1903 JCExpression methodReferenceReceiver; 1904 1905 LambdaTranslationContext(JCLambda tree) { 1906 super(tree); 1907 Frame frame = frameStack.head; 1908 switch (frame.tree.getTag()) { 1909 case VARDEF: 1910 assignedTo = self = ((JCVariableDecl) frame.tree).sym; 1911 break; 1912 case ASSIGN: 1913 self = null; 1914 assignedTo = TreeInfo.symbol(((JCAssign) frame.tree).getVariable()); 1915 break; 1916 default: 1917 assignedTo = self = null; 1918 break; 1919 } 1920 1921 // This symbol will be filled-in in complete 1922 this.translatedSym = makePrivateSyntheticMethod(0, null, null, owner.enclClass()); 1923 1924 translatedSymbols = new EnumMap<>(LambdaSymbolKind.class); 1925 1926 translatedSymbols.put(PARAM, new LinkedHashMap<Symbol, Symbol>()); 1927 translatedSymbols.put(LOCAL_VAR, new LinkedHashMap<Symbol, Symbol>()); 1928 translatedSymbols.put(CAPTURED_VAR, new LinkedHashMap<Symbol, Symbol>()); 1929 translatedSymbols.put(CAPTURED_THIS, new LinkedHashMap<Symbol, Symbol>()); 1930 translatedSymbols.put(CAPTURED_OUTER_THIS, new LinkedHashMap<Symbol, Symbol>()); 1931 translatedSymbols.put(TYPE_VAR, new LinkedHashMap<Symbol, Symbol>()); 1932 1933 freeVarProcessedLocalClasses = new HashSet<>(); 1934 } 1935 1936 /** 1937 * For a serializable lambda, generate a disambiguating string 1938 * which maximizes stability across deserialization. 1939 * 1940 * @return String to differentiate synthetic lambda method names 1941 */ 1942 private String serializedLambdaDisambiguation() { 1943 StringBuilder buf = new StringBuilder(); 1944 // Append the enclosing method signature to differentiate 1945 // overloaded enclosing methods. For lambdas enclosed in 1946 // lambdas, the generated lambda method will not have type yet, 1947 // but the enclosing method's name will have been generated 1948 // with this same method, so it will be unique and never be 1949 // overloaded. 1950 Assert.check( 1951 owner.type != null || 1952 directlyEnclosingLambda() != null); 1953 if (owner.type != null) { 1954 buf.append(typeSig(owner.type)); 1955 buf.append(":"); 1956 } 1957 1958 // Add target type info 1959 buf.append(types.findDescriptorSymbol(tree.type.tsym).owner.flatName()); 1960 buf.append(" "); 1961 1962 // Add variable assigned to 1963 if (assignedTo != null) { 1964 buf.append(assignedTo.flatName()); 1965 buf.append("="); 1966 } 1967 //add captured locals info: type, name, order 1968 for (Symbol fv : getSymbolMap(CAPTURED_VAR).keySet()) { 1969 if (fv != self) { 1970 buf.append(typeSig(fv.type)); 1971 buf.append(" "); 1972 buf.append(fv.flatName()); 1973 buf.append(","); 1974 } 1975 } 1976 1977 return buf.toString(); 1978 } 1979 1980 /** 1981 * For a non-serializable lambda, generate a simple method. 1982 * 1983 * @return Name to use for the synthetic lambda method name 1984 */ 1985 private Name lambdaName() { 1986 return names.lambda.append(names.fromString(enclosingMethodName() + "$" + lambdaCount++)); 1987 } 1988 1989 /** 1990 * For a serializable lambda, generate a method name which maximizes 1991 * name stability across deserialization. 1992 * 1993 * @return Name to use for the synthetic lambda method name 1994 */ 1995 private Name serializedLambdaName() { 1996 StringBuilder buf = new StringBuilder(); 1997 buf.append(names.lambda); 1998 // Append the name of the method enclosing the lambda. 1999 buf.append(enclosingMethodName()); 2000 buf.append('$'); 2001 // Append a hash of the disambiguating string : enclosing method 2002 // signature, etc. 2003 String disam = serializedLambdaDisambiguation(); 2004 buf.append(Integer.toHexString(disam.hashCode())); 2005 buf.append('$'); 2006 // The above appended name components may not be unique, append 2007 // a count based on the above name components. 2008 buf.append(syntheticMethodNameCounts.getIndex(buf)); 2009 String result = buf.toString(); 2010 //System.err.printf("serializedLambdaName: %s -- %s\n", result, disam); 2011 return names.fromString(result); 2012 } 2013 2014 /** 2015 * Translate a symbol of a given kind into something suitable for the 2016 * synthetic lambda body 2017 */ 2018 Symbol translate(final Symbol sym, LambdaSymbolKind skind) { 2019 Symbol ret; 2020 switch (skind) { 2021 case CAPTURED_THIS: 2022 ret = sym; // self represented 2023 break; 2024 case TYPE_VAR: 2025 // Just erase the type var 2026 ret = new VarSymbol(sym.flags(), sym.name, 2027 types.erasure(sym.type), sym.owner); 2028 2029 /* this information should also be kept for LVT generation at Gen 2030 * a Symbol with pos < startPos won't be tracked. 2031 */ 2032 ((VarSymbol)ret).pos = ((VarSymbol)sym).pos; 2033 break; 2034 case CAPTURED_VAR: 2035 ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, sym.name, types.erasure(sym.type), translatedSym) { 2036 @Override 2037 public Symbol baseSymbol() { 2038 //keep mapping with original captured symbol 2039 return sym; 2040 } 2041 }; 2042 break; 2043 case CAPTURED_OUTER_THIS: 2044 Name name = names.fromString(new String(sym.flatName().toString().replace('.', '$') + names.dollarThis)); 2045 ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym) { 2046 @Override 2047 public Symbol baseSymbol() { 2048 //keep mapping with original captured symbol 2049 return sym; 2050 } 2051 }; 2052 break; 2053 case LOCAL_VAR: 2054 ret = new VarSymbol(sym.flags() & FINAL, sym.name, sym.type, translatedSym); 2055 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos; 2056 break; 2057 case PARAM: 2058 ret = new VarSymbol((sym.flags() & FINAL) | PARAMETER, sym.name, types.erasure(sym.type), translatedSym); 2059 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos; 2060 break; 2061 default: 2062 Assert.error(skind.name()); 2063 throw new AssertionError(); 2064 } 2065 if (ret != sym) { 2066 ret.setDeclarationAttributes(sym.getRawAttributes()); 2067 ret.setTypeAttributes(sym.getRawTypeAttributes()); 2068 } 2069 return ret; 2070 } 2071 2072 void addSymbol(Symbol sym, LambdaSymbolKind skind) { 2073 if (skind == CAPTURED_THIS && sym != null && sym.kind == TYP && !typesUnderConstruction.isEmpty()) { 2074 ClassSymbol currentClass = currentClass(); 2075 if (currentClass != null && typesUnderConstruction.contains(currentClass)) { 2076 // reference must be to enclosing outer instance, mutate capture kind. 2077 Assert.check(sym != currentClass); // should have been caught right in Attr 2078 skind = CAPTURED_OUTER_THIS; 2079 } 2080 } 2081 Map<Symbol, Symbol> transMap = getSymbolMap(skind); 2082 if (!transMap.containsKey(sym)) { 2083 transMap.put(sym, translate(sym, skind)); 2084 } 2085 } 2086 2087 Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind skind) { 2088 Map<Symbol, Symbol> m = translatedSymbols.get(skind); 2089 Assert.checkNonNull(m); 2090 return m; 2091 } 2092 2093 JCTree translate(JCIdent lambdaIdent) { 2094 for (LambdaSymbolKind kind : LambdaSymbolKind.values()) { 2095 Map<Symbol, Symbol> m = getSymbolMap(kind); 2096 switch(kind) { 2097 default: 2098 if (m.containsKey(lambdaIdent.sym)) { 2099 Symbol tSym = m.get(lambdaIdent.sym); 2100 JCTree t = make.Ident(tSym).setType(lambdaIdent.type); 2101 tSym.setTypeAttributes(lambdaIdent.sym.getRawTypeAttributes()); 2102 return t; 2103 } 2104 break; 2105 case CAPTURED_OUTER_THIS: 2106 if (lambdaIdent.sym.owner.kind == TYP && m.containsKey(lambdaIdent.sym.owner)) { 2107 // Transform outer instance variable references anchoring them to the captured synthetic. 2108 Symbol tSym = m.get(lambdaIdent.sym.owner); 2109 JCExpression t = make.Ident(tSym).setType(lambdaIdent.sym.owner.type); 2110 tSym.setTypeAttributes(lambdaIdent.sym.owner.getRawTypeAttributes()); 2111 t = make.Select(t, lambdaIdent.name); 2112 t.setType(lambdaIdent.type); 2113 TreeInfo.setSymbol(t, lambdaIdent.sym); 2114 return t; 2115 } 2116 break; 2117 } 2118 } 2119 return null; 2120 } 2121 2122 /* Translate away qualified this expressions, anchoring them to synthetic parameters that 2123 capture the qualified this handle. `fieldAccess' is guaranteed to one such. 2124 */ 2125 public JCTree translate(JCFieldAccess fieldAccess) { 2126 Assert.check(fieldAccess.name == names._this); 2127 Map<Symbol, Symbol> m = translatedSymbols.get(LambdaSymbolKind.CAPTURED_OUTER_THIS); 2128 if (m.containsKey(fieldAccess.sym.owner)) { 2129 Symbol tSym = m.get(fieldAccess.sym.owner); 2130 JCExpression t = make.Ident(tSym).setType(fieldAccess.sym.owner.type); 2131 tSym.setTypeAttributes(fieldAccess.sym.owner.getRawTypeAttributes()); 2132 return t; 2133 } 2134 return null; 2135 } 2136 2137 /** 2138 * The translatedSym is not complete/accurate until the analysis is 2139 * finished. Once the analysis is finished, the translatedSym is 2140 * "completed" -- updated with type information, access modifiers, 2141 * and full parameter list. 2142 */ 2143 void complete() { 2144 if (syntheticParams != null) { 2145 return; 2146 } 2147 boolean inInterface = translatedSym.owner.isInterface(); 2148 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty(); 2149 2150 // If instance access isn't needed, make it static. 2151 // Interface instance methods must be default methods. 2152 // Lambda methods are private synthetic. 2153 // Inherit ACC_STRICT from the enclosing method, or, for clinit, 2154 // from the class. 2155 translatedSym.flags_field = SYNTHETIC | LAMBDA_METHOD | 2156 owner.flags_field & STRICTFP | 2157 owner.owner.flags_field & STRICTFP | 2158 PRIVATE | 2159 (thisReferenced? (inInterface? DEFAULT : 0) : STATIC); 2160 2161 //compute synthetic params 2162 ListBuffer<JCVariableDecl> params = new ListBuffer<>(); 2163 ListBuffer<VarSymbol> parameterSymbols = new ListBuffer<>(); 2164 2165 // The signature of the method is augmented with the following 2166 // synthetic parameters: 2167 // 2168 // 1) reference to enclosing contexts captured by the lambda expression 2169 // 2) enclosing locals captured by the lambda expression 2170 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR).values()) { 2171 params.append(make.VarDef((VarSymbol) thisSym, null)); 2172 parameterSymbols.append((VarSymbol) thisSym); 2173 } 2174 for (Symbol thisSym : getSymbolMap(CAPTURED_OUTER_THIS).values()) { 2175 params.append(make.VarDef((VarSymbol) thisSym, null)); 2176 parameterSymbols.append((VarSymbol) thisSym); 2177 } 2178 for (Symbol thisSym : getSymbolMap(PARAM).values()) { 2179 params.append(make.VarDef((VarSymbol) thisSym, null)); 2180 parameterSymbols.append((VarSymbol) thisSym); 2181 } 2182 syntheticParams = params.toList(); 2183 2184 translatedSym.params = parameterSymbols.toList(); 2185 2186 // Compute and set the lambda name 2187 translatedSym.name = isSerializable() 2188 ? serializedLambdaName() 2189 : lambdaName(); 2190 2191 //prepend synthetic args to translated lambda method signature 2192 translatedSym.type = types.createMethodTypeWithParameters( 2193 generatedLambdaSig(), 2194 TreeInfo.types(syntheticParams)); 2195 } 2196 2197 Type generatedLambdaSig() { 2198 return types.erasure(tree.getDescriptorType(types)); 2199 } 2200 } 2201 2202 /** 2203 * This class retains all the useful information about a method reference; 2204 * the contents of this class are filled by the LambdaAnalyzer visitor, 2205 * and the used by the main translation routines in order to adjust method 2206 * references (i.e. in case a bridge is needed) 2207 */ 2208 final class ReferenceTranslationContext extends TranslationContext<JCMemberReference> { 2209 2210 final boolean isSuper; 2211 final Symbol sigPolySym; 2212 2213 ReferenceTranslationContext(JCMemberReference tree) { 2214 super(tree); 2215 this.isSuper = tree.hasKind(ReferenceKind.SUPER); 2216 this.sigPolySym = isSignaturePolymorphic() 2217 ? makePrivateSyntheticMethod(tree.sym.flags(), 2218 tree.sym.name, 2219 bridgedRefSig(), 2220 tree.sym.enclClass()) 2221 : null; 2222 } 2223 2224 /** 2225 * Get the opcode associated with this method reference 2226 */ 2227 int referenceKind() { 2228 return LambdaToMethod.this.referenceKind(tree.sym); 2229 } 2230 2231 boolean needsVarArgsConversion() { 2232 return tree.varargsElement != null; 2233 } 2234 2235 /** 2236 * @return Is this an array operation like clone() 2237 */ 2238 boolean isArrayOp() { 2239 return tree.sym.owner == syms.arrayClass; 2240 } 2241 2242 boolean receiverAccessible() { 2243 //hack needed to workaround 292 bug (7087658) 2244 //when 292 issue is fixed we should remove this and change the backend 2245 //code to always generate a method handle to an accessible method 2246 return tree.ownerAccessible; 2247 } 2248 2249 /** 2250 * The VM does not support access across nested classes (8010319). 2251 * Were that ever to change, this should be removed. 2252 */ 2253 boolean isPrivateInOtherClass() { 2254 return (tree.sym.flags() & PRIVATE) != 0 && 2255 !types.isSameType( 2256 types.erasure(tree.sym.enclClass().asType()), 2257 types.erasure(owner.enclClass().asType())); 2258 } 2259 2260 boolean isProtectedInSuperClassOfEnclosingClassInOtherPackage() { 2261 return ((tree.sym.flags() & PROTECTED) != 0 && 2262 tree.sym.packge() != owner.packge() && 2263 !owner.enclClass().isSubClass(tree.sym.owner, types)); 2264 } 2265 2266 /** 2267 * Signature polymorphic methods need special handling. 2268 * e.g. MethodHandle.invoke() MethodHandle.invokeExact() 2269 */ 2270 final boolean isSignaturePolymorphic() { 2271 return tree.sym.kind == MTH && 2272 types.isSignaturePolymorphic((MethodSymbol)tree.sym); 2273 } 2274 2275 /** 2276 * Erasure destroys the implementation parameter subtype 2277 * relationship for intersection types 2278 */ 2279 boolean interfaceParameterIsIntersectionType() { 2280 List<Type> tl = tree.getDescriptorType(types).getParameterTypes(); 2281 for (; tl.nonEmpty(); tl = tl.tail) { 2282 Type pt = tl.head; 2283 if (pt.getKind() == TypeKind.TYPEVAR) { 2284 TypeVar tv = (TypeVar) pt; 2285 if (tv.bound.getKind() == TypeKind.INTERSECTION) { 2286 return true; 2287 } 2288 } 2289 } 2290 return false; 2291 } 2292 2293 /** 2294 * Does this reference need to be converted to a lambda 2295 * (i.e. var args need to be expanded or "super" is used) 2296 */ 2297 final boolean needsConversionToLambda() { 2298 return interfaceParameterIsIntersectionType() || 2299 isSuper || 2300 needsVarArgsConversion() || 2301 isArrayOp() || 2302 isPrivateInOtherClass() || 2303 isProtectedInSuperClassOfEnclosingClassInOtherPackage() || 2304 !receiverAccessible() || 2305 (tree.getMode() == ReferenceMode.NEW && 2306 tree.kind != ReferenceKind.ARRAY_CTOR && 2307 (tree.sym.owner.isLocal() || tree.sym.owner.isInner())); 2308 } 2309 2310 Type generatedRefSig() { 2311 return types.erasure(tree.sym.type); 2312 } 2313 2314 Type bridgedRefSig() { 2315 return types.erasure(types.findDescriptorSymbol(tree.targets.head.tsym).type); 2316 } 2317 } 2318 } 2319 // </editor-fold> 2320 2321 /* 2322 * These keys provide mappings for various translated lambda symbols 2323 * and the prevailing order must be maintained. 2324 */ 2325 enum LambdaSymbolKind { 2326 PARAM, // original to translated lambda parameters 2327 LOCAL_VAR, // original to translated lambda locals 2328 CAPTURED_VAR, // variables in enclosing scope to translated synthetic parameters 2329 CAPTURED_THIS, // class symbols to translated synthetic parameters (for captured member access) 2330 CAPTURED_OUTER_THIS, // used when `this' capture is illegal, but outer this capture is legit (JDK-8129740) 2331 TYPE_VAR // original to translated lambda type variables 2332 } 2333 2334 /** 2335 * **************************************************************** 2336 * Signature Generation 2337 * **************************************************************** 2338 */ 2339 2340 private String typeSig(Type type) { 2341 L2MSignatureGenerator sg = new L2MSignatureGenerator(); 2342 sg.assembleSig(type); 2343 return sg.toString(); 2344 } 2345 2346 private String classSig(Type type) { 2347 L2MSignatureGenerator sg = new L2MSignatureGenerator(); 2348 sg.assembleClassSig(type); 2349 return sg.toString(); 2350 } 2351 2352 /** 2353 * Signature Generation 2354 */ 2355 private class L2MSignatureGenerator extends Types.SignatureGenerator { 2356 2357 /** 2358 * An output buffer for type signatures. 2359 */ 2360 StringBuilder sb = new StringBuilder(); 2361 2362 L2MSignatureGenerator() { 2363 super(types); 2364 } 2365 2366 @Override 2367 protected void append(char ch) { 2368 sb.append(ch); 2369 } 2370 2371 @Override 2372 protected void append(byte[] ba) { 2373 sb.append(new String(ba)); 2374 } 2375 2376 @Override 2377 protected void append(Name name) { 2378 sb.append(name.toString()); 2379 } 2380 2381 @Override 2382 public String toString() { 2383 return sb.toString(); 2384 } 2385 } 2386} 2387