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