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