Fri, 14 Nov 2014 21:10:58 -0800
8048121: javac complex method references: revamp and simplify
8037404: javac NPE or VerifyError for code with constructor reference of inner class
8047341: lambda reference to inner class in base class causes LambdaConversionException
8044748: JVM cannot access constructor though ::new reference although can call it directly
8044737: Lambda: NPE while obtaining method reference through lambda expression
8038776: VerifyError when running successfully compiled java class
Reviewed-by: dlsmith, vromero
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 */
25 package com.sun.tools.javac.comp;
27 import com.sun.tools.javac.tree.*;
28 import com.sun.tools.javac.tree.JCTree.*;
29 import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind;
30 import com.sun.tools.javac.tree.TreeMaker;
31 import com.sun.tools.javac.tree.TreeTranslator;
32 import com.sun.tools.javac.code.Attribute;
33 import com.sun.tools.javac.code.Kinds;
34 import com.sun.tools.javac.code.Scope;
35 import com.sun.tools.javac.code.Symbol;
36 import com.sun.tools.javac.code.Symbol.ClassSymbol;
37 import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol;
38 import com.sun.tools.javac.code.Symbol.MethodSymbol;
39 import com.sun.tools.javac.code.Symbol.TypeSymbol;
40 import com.sun.tools.javac.code.Symbol.VarSymbol;
41 import com.sun.tools.javac.code.Symtab;
42 import com.sun.tools.javac.code.Type;
43 import com.sun.tools.javac.code.Type.MethodType;
44 import com.sun.tools.javac.code.Types;
45 import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzerPreprocessor.*;
46 import com.sun.tools.javac.comp.Lower.BasicFreeVarCollector;
47 import com.sun.tools.javac.jvm.*;
48 import com.sun.tools.javac.util.*;
49 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
50 import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
52 import java.util.EnumMap;
53 import java.util.HashMap;
54 import java.util.HashSet;
55 import java.util.LinkedHashMap;
56 import java.util.Map;
57 import java.util.Set;
59 import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*;
60 import static com.sun.tools.javac.code.Flags.*;
61 import static com.sun.tools.javac.code.Kinds.*;
62 import static com.sun.tools.javac.code.TypeTag.*;
63 import static com.sun.tools.javac.tree.JCTree.Tag.*;
65 /**
66 * This pass desugars lambda expressions into static methods
67 *
68 * <p><b>This is NOT part of any supported API.
69 * If you write code that depends on this, you do so at your own risk.
70 * This code and its internal interfaces are subject to change or
71 * deletion without notice.</b>
72 */
73 public class LambdaToMethod extends TreeTranslator {
75 private Attr attr;
76 private JCDiagnostic.Factory diags;
77 private Log log;
78 private Lower lower;
79 private Names names;
80 private Symtab syms;
81 private Resolve rs;
82 private TreeMaker make;
83 private Types types;
84 private TransTypes transTypes;
85 private Env<AttrContext> attrEnv;
87 /** the analyzer scanner */
88 private LambdaAnalyzerPreprocessor analyzer;
90 /** map from lambda trees to translation contexts */
91 private Map<JCTree, TranslationContext<?>> contextMap;
93 /** current translation context (visitor argument) */
94 private TranslationContext<?> context;
96 /** info about the current class being processed */
97 private KlassInfo kInfo;
99 /** dump statistics about lambda code generation */
100 private boolean dumpLambdaToMethodStats;
102 /** force serializable representation, for stress testing **/
103 private final boolean forceSerializable;
105 /** Flag for alternate metafactories indicating the lambda object is intended to be serializable */
106 public static final int FLAG_SERIALIZABLE = 1 << 0;
108 /** Flag for alternate metafactories indicating the lambda object has multiple targets */
109 public static final int FLAG_MARKERS = 1 << 1;
111 /** Flag for alternate metafactories indicating the lambda object requires multiple bridges */
112 public static final int FLAG_BRIDGES = 1 << 2;
114 // <editor-fold defaultstate="collapsed" desc="Instantiating">
115 protected static final Context.Key<LambdaToMethod> unlambdaKey =
116 new Context.Key<LambdaToMethod>();
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>
144 private class KlassInfo {
146 /**
147 * list of methods to append
148 */
149 private ListBuffer<JCTree> appendedMethodList;
151 /**
152 * list of deserialization cases
153 */
154 private final Map<String, ListBuffer<JCStatement>> deserializeCases;
156 /**
157 * deserialize method symbol
158 */
159 private final MethodSymbol deserMethodSym;
161 /**
162 * deserialize method parameter symbol
163 */
164 private final VarSymbol deserParamSym;
166 private final JCClassDecl clazz;
168 private KlassInfo(JCClassDecl clazz) {
169 this.clazz = clazz;
170 appendedMethodList = new ListBuffer<>();
171 deserializeCases = new HashMap<String, ListBuffer<JCStatement>>();
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 }
179 private void addMethod(JCTree decl) {
180 appendedMethodList = appendedMethodList.prepend(decl);
181 }
182 }
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 }
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 }
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 }
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<JCTree, TranslationContext<?>>();
215 return translate(cdef);
216 }
217 // </editor-fold>
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 }
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;
269 {
270 Symbol owner = localContext.owner;
271 ListBuffer<Attribute.TypeCompound> ownerTypeAnnos = new ListBuffer<Attribute.TypeCompound>();
272 ListBuffer<Attribute.TypeCompound> lambdaTypeAnnos = new ListBuffer<Attribute.TypeCompound>();
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 }
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;
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));
308 //Add the method to the list of methods to be added to this class.
309 kInfo.addMethod(lambdaDecl);
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
322 ListBuffer<JCExpression> syntheticInits = new ListBuffer<>();
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 }
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 }
340 //then, determine the arguments to the indy call
341 List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev);
343 //build a sam instance using an indy call to the meta-factory
344 int refKind = referenceKind(sym);
346 //convert to an invokedynamic call
347 result = makeMetafactoryIndyCall(context, refKind, sym, indy_args);
348 }
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 }
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;
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;
373 //the qualifying expression is treated as a special captured arg
374 JCExpression init;
375 switch(tree.kind) {
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;
384 case BOUND: /** Expr :: instMethod */
385 init = tree.getQualifierExpression();
386 init = attr.makeNullCheck(init);
387 break;
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;
396 default:
397 throw new InternalError("Should not have an invalid kind");
398 }
400 List<JCExpression> indy_args = init==null? List.<JCExpression>nil() : translate(List.of(init), localContext.prev);
403 //build a sam instance using an indy call to the meta-factory
404 result = makeMetafactoryIndyCall(localContext, localContext.referenceKind(), refSym, indy_args);
405 }
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);
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 }
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 Scope 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 }
462 // </editor-fold>
464 // <editor-fold defaultstate="collapsed" desc="Translation helper methods">
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 }
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 }
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);
507 class LambdaBodyTranslator extends TreeTranslator {
509 @Override
510 public void visitClassDef(JCClassDecl tree) {
511 //do NOT recurse on any inner classes
512 result = tree;
513 }
515 @Override
516 public void visitLambda(JCLambda tree) {
517 //do NOT recurse on any nested lambdas
518 result = tree;
519 }
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 }
539 }
540 }
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 }
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 }
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 }
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 }
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));
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 }
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 }
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 }
676 private JCExpression deserGetter(String func, Type type) {
677 return deserGetter(func, type, List.<Type>nil(), List.<JCExpression>nil());
678 }
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 }
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 }
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 }
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 }
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 }
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 == Kinds.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 }
739 // </editor-fold>
741 /**
742 * Converts a method reference which cannot be used directly into a lambda
743 */
744 private class MemberReferenceToLambda {
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<>();
752 private JCExpression receiverExpression = null;
754 MemberReferenceToLambda(JCMemberReference tree, ReferenceTranslationContext localContext, Symbol owner) {
755 this.tree = tree;
756 this.localContext = localContext;
757 this.owner = owner;
758 }
760 JCLambda lambda() {
761 int prevPos = make.pos;
762 try {
763 make.at(tree);
764 Type samDesc = localContext.bridgedRefSig();
765 List<Type> samPTypes = samDesc.getParameterTypes();
767 // an extra argument is prepended in the case where the member
768 // reference is an unbound instance method reference (in which
769 // case the receiver expression in passed.
770 VarSymbol rcvr;
771 switch (tree.kind) {
772 case BOUND:
773 rcvr = addParameter("rec$", tree.getQualifierExpression().type, false);
774 receiverExpression = attr.makeNullCheck(tree.getQualifierExpression());
775 break;
776 case UNBOUND:
777 rcvr = addParameter("rec$", samPTypes.head, false);
778 samPTypes = samPTypes.tail;
779 break;
780 default:
781 rcvr = null;
782 break;
783 }
785 // generate the parameter list for the coverted member reference.
786 // the signature will match the signature of the target sam descriptor
788 List<Type> refPTypes = tree.sym.type.getParameterTypes();
789 int refSize = refPTypes.size();
790 int samSize = samPTypes.size();
791 // Last parameter to copy from referenced method
792 int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize;
794 List<Type> l = refPTypes;
795 // Use parameter types of the referenced method, excluding final var args
796 for (int i = 0; l.nonEmpty() && i < last; ++i) {
797 addParameter("x$" + i, l.head, true);
798 l = l.tail;
799 }
800 // Flatten out the var args
801 for (int i = last; i < samSize; ++i) {
802 addParameter("xva$" + i, tree.varargsElement, true);
803 }
805 //body generation - this can be either a method call or a
806 //new instance creation expression, depending on the member reference kind
807 JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE)
808 ? expressionInvoke(rcvr)
809 : expressionNew();
811 JCLambda slam = make.Lambda(params.toList(), expr);
812 slam.targets = tree.targets;
813 slam.type = tree.type;
814 slam.pos = tree.pos;
815 return slam;
816 } finally {
817 make.at(prevPos);
818 }
819 }
821 JCExpression getReceiverExpression() {
822 return receiverExpression;
823 }
825 private JCExpression makeReceiver(VarSymbol rcvr) {
826 if (rcvr == null) return null;
827 JCExpression rcvrExpr = make.Ident(rcvr);
828 Type rcvrType = tree.sym.enclClass().type;
829 if (rcvrType == syms.arrayClass.type) {
830 // Map the receiver type to the actually type, not just "array"
831 rcvrType = tree.getQualifierExpression().type;
832 }
833 if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) {
834 rcvrExpr = make.TypeCast(make.Type(rcvrType), rcvrExpr).setType(rcvrType);
835 }
836 return rcvrExpr;
837 }
839 /**
840 * determine the receiver of the method call - the receiver can
841 * be a type qualifier, the synthetic receiver parameter or 'super'.
842 */
843 private JCExpression expressionInvoke(VarSymbol rcvr) {
844 JCExpression qualifier =
845 tree.sym.isStatic() ?
846 make.Type(tree.sym.owner.type) :
847 (rcvr != null) ?
848 makeReceiver(rcvr) :
849 tree.getQualifierExpression();
851 //create the qualifier expression
852 JCFieldAccess select = make.Select(qualifier, tree.sym.name);
853 select.sym = tree.sym;
854 select.type = tree.sym.erasure(types);
856 //create the method call expression
857 JCExpression apply = make.Apply(List.<JCExpression>nil(), select,
858 convertArgs(tree.sym, args.toList(), tree.varargsElement)).
859 setType(tree.sym.erasure(types).getReturnType());
861 apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType());
862 setVarargsIfNeeded(apply, tree.varargsElement);
863 return apply;
864 }
866 /**
867 * Lambda body to use for a 'new'.
868 */
869 private JCExpression expressionNew() {
870 if (tree.kind == ReferenceKind.ARRAY_CTOR) {
871 //create the array creation expression
872 JCNewArray newArr = make.NewArray(
873 make.Type(types.elemtype(tree.getQualifierExpression().type)),
874 List.of(make.Ident(params.first())),
875 null);
876 newArr.type = tree.getQualifierExpression().type;
877 return newArr;
878 } else {
879 //create the instance creation expression
880 //note that method reference syntax does not allow an explicit
881 //enclosing class (so the enclosing class is null)
882 JCNewClass newClass = make.NewClass(null,
883 List.<JCExpression>nil(),
884 make.Type(tree.getQualifierExpression().type),
885 convertArgs(tree.sym, args.toList(), tree.varargsElement),
886 null);
887 newClass.constructor = tree.sym;
888 newClass.constructorType = tree.sym.erasure(types);
889 newClass.type = tree.getQualifierExpression().type;
890 setVarargsIfNeeded(newClass, tree.varargsElement);
891 return newClass;
892 }
893 }
895 private VarSymbol addParameter(String name, Type p, boolean genArg) {
896 VarSymbol vsym = new VarSymbol(PARAMETER | SYNTHETIC, names.fromString(name), p, owner);
897 vsym.pos = tree.pos;
898 params.append(make.VarDef(vsym, null));
899 if (genArg) {
900 args.append(make.Ident(vsym));
901 }
902 return vsym;
903 }
904 }
906 private MethodType typeToMethodType(Type mt) {
907 Type type = types.erasure(mt);
908 return new MethodType(type.getParameterTypes(),
909 type.getReturnType(),
910 type.getThrownTypes(),
911 syms.methodClass);
912 }
914 /**
915 * Generate an indy method call to the meta factory
916 */
917 private JCExpression makeMetafactoryIndyCall(TranslationContext<?> context,
918 int refKind, Symbol refSym, List<JCExpression> indy_args) {
919 JCFunctionalExpression tree = context.tree;
920 //determine the static bsm args
921 MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
922 List<Object> staticArgs = List.<Object>of(
923 typeToMethodType(samSym.type),
924 new Pool.MethodHandle(refKind, refSym, types),
925 typeToMethodType(tree.getDescriptorType(types)));
927 //computed indy arg types
928 ListBuffer<Type> indy_args_types = new ListBuffer<>();
929 for (JCExpression arg : indy_args) {
930 indy_args_types.append(arg.type);
931 }
933 //finally, compute the type of the indy call
934 MethodType indyType = new MethodType(indy_args_types.toList(),
935 tree.type,
936 List.<Type>nil(),
937 syms.methodClass);
939 Name metafactoryName = context.needsAltMetafactory() ?
940 names.altMetafactory : names.metafactory;
942 if (context.needsAltMetafactory()) {
943 ListBuffer<Object> markers = new ListBuffer<>();
944 for (Type t : tree.targets.tail) {
945 if (t.tsym != syms.serializableType.tsym) {
946 markers.append(t.tsym);
947 }
948 }
949 int flags = context.isSerializable() ? FLAG_SERIALIZABLE : 0;
950 boolean hasMarkers = markers.nonEmpty();
951 boolean hasBridges = context.bridges.nonEmpty();
952 if (hasMarkers) {
953 flags |= FLAG_MARKERS;
954 }
955 if (hasBridges) {
956 flags |= FLAG_BRIDGES;
957 }
958 staticArgs = staticArgs.append(flags);
959 if (hasMarkers) {
960 staticArgs = staticArgs.append(markers.length());
961 staticArgs = staticArgs.appendList(markers.toList());
962 }
963 if (hasBridges) {
964 staticArgs = staticArgs.append(context.bridges.length() - 1);
965 for (Symbol s : context.bridges) {
966 Type s_erasure = s.erasure(types);
967 if (!types.isSameType(s_erasure, samSym.erasure(types))) {
968 staticArgs = staticArgs.append(s.erasure(types));
969 }
970 }
971 }
972 if (context.isSerializable()) {
973 int prevPos = make.pos;
974 try {
975 make.at(kInfo.clazz);
976 addDeserializationCase(refKind, refSym, tree.type, samSym,
977 tree, staticArgs, indyType);
978 } finally {
979 make.at(prevPos);
980 }
981 }
982 }
984 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args, samSym.name);
985 }
987 /**
988 * Generate an indy method call with given name, type and static bootstrap
989 * arguments types
990 */
991 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
992 List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs,
993 Name methName) {
994 int prevPos = make.pos;
995 try {
996 make.at(pos);
997 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
998 syms.stringType,
999 syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs));
1001 Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site,
1002 bsmName, bsm_staticArgs, List.<Type>nil());
1004 DynamicMethodSymbol dynSym =
1005 new DynamicMethodSymbol(methName,
1006 syms.noSymbol,
1007 bsm.isStatic() ?
1008 ClassFile.REF_invokeStatic :
1009 ClassFile.REF_invokeVirtual,
1010 (MethodSymbol)bsm,
1011 indyType,
1012 staticArgs.toArray());
1014 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName);
1015 qualifier.sym = dynSym;
1016 qualifier.type = indyType.getReturnType();
1018 JCMethodInvocation proxyCall = make.Apply(List.<JCExpression>nil(), qualifier, indyArgs);
1019 proxyCall.type = indyType.getReturnType();
1020 return proxyCall;
1021 } finally {
1022 make.at(prevPos);
1023 }
1024 }
1025 //where
1026 private List<Type> bsmStaticArgToTypes(List<Object> args) {
1027 ListBuffer<Type> argtypes = new ListBuffer<>();
1028 for (Object arg : args) {
1029 argtypes.append(bsmStaticArgToType(arg));
1030 }
1031 return argtypes.toList();
1032 }
1034 private Type bsmStaticArgToType(Object arg) {
1035 Assert.checkNonNull(arg);
1036 if (arg instanceof ClassSymbol) {
1037 return syms.classType;
1038 } else if (arg instanceof Integer) {
1039 return syms.intType;
1040 } else if (arg instanceof Long) {
1041 return syms.longType;
1042 } else if (arg instanceof Float) {
1043 return syms.floatType;
1044 } else if (arg instanceof Double) {
1045 return syms.doubleType;
1046 } else if (arg instanceof String) {
1047 return syms.stringType;
1048 } else if (arg instanceof Pool.MethodHandle) {
1049 return syms.methodHandleType;
1050 } else if (arg instanceof MethodType) {
1051 return syms.methodTypeType;
1052 } else {
1053 Assert.error("bad static arg " + arg.getClass());
1054 return null;
1055 }
1056 }
1058 /**
1059 * Get the opcode associated with this method reference
1060 */
1061 private int referenceKind(Symbol refSym) {
1062 if (refSym.isConstructor()) {
1063 return ClassFile.REF_newInvokeSpecial;
1064 } else {
1065 if (refSym.isStatic()) {
1066 return ClassFile.REF_invokeStatic;
1067 } else if ((refSym.flags() & PRIVATE) != 0) {
1068 return ClassFile.REF_invokeSpecial;
1069 } else if (refSym.enclClass().isInterface()) {
1070 return ClassFile.REF_invokeInterface;
1071 } else {
1072 return ClassFile.REF_invokeVirtual;
1073 }
1074 }
1075 }
1077 // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer">
1078 /**
1079 * This visitor collects information about translation of a lambda expression.
1080 * More specifically, it keeps track of the enclosing contexts and captured locals
1081 * accessed by the lambda being translated (as well as other useful info).
1082 * It also translates away problems for LambdaToMethod.
1083 */
1084 class LambdaAnalyzerPreprocessor extends TreeTranslator {
1086 /** the frame stack - used to reconstruct translation info about enclosing scopes */
1087 private List<Frame> frameStack;
1089 /**
1090 * keep the count of lambda expression (used to generate unambiguous
1091 * names)
1092 */
1093 private int lambdaCount = 0;
1095 /**
1096 * keep the count of lambda expression defined in given context (used to
1097 * generate unambiguous names for serializable lambdas)
1098 */
1099 private class SyntheticMethodNameCounter {
1100 private Map<String, Integer> map = new HashMap<>();
1101 int getIndex(StringBuilder buf) {
1102 String temp = buf.toString();
1103 Integer count = map.get(temp);
1104 if (count == null) {
1105 count = 0;
1106 }
1107 ++count;
1108 map.put(temp, count);
1109 return count;
1110 }
1111 }
1112 private SyntheticMethodNameCounter syntheticMethodNameCounts =
1113 new SyntheticMethodNameCounter();
1115 private Map<Symbol, JCClassDecl> localClassDefs;
1117 /**
1118 * maps for fake clinit symbols to be used as owners of lambda occurring in
1119 * a static var init context
1120 */
1121 private Map<ClassSymbol, Symbol> clinits =
1122 new HashMap<ClassSymbol, Symbol>();
1124 private JCClassDecl analyzeAndPreprocessClass(JCClassDecl tree) {
1125 frameStack = List.nil();
1126 localClassDefs = new HashMap<Symbol, JCClassDecl>();
1127 return translate(tree);
1128 }
1130 @Override
1131 public void visitBlock(JCBlock tree) {
1132 List<Frame> prevStack = frameStack;
1133 try {
1134 if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) {
1135 frameStack = frameStack.prepend(new Frame(tree));
1136 }
1137 super.visitBlock(tree);
1138 }
1139 finally {
1140 frameStack = prevStack;
1141 }
1142 }
1144 @Override
1145 public void visitClassDef(JCClassDecl tree) {
1146 List<Frame> prevStack = frameStack;
1147 SyntheticMethodNameCounter prevSyntheticMethodNameCounts =
1148 syntheticMethodNameCounts;
1149 Map<ClassSymbol, Symbol> prevClinits = clinits;
1150 DiagnosticSource prevSource = log.currentSource();
1151 try {
1152 log.useSource(tree.sym.sourcefile);
1153 syntheticMethodNameCounts = new SyntheticMethodNameCounter();
1154 prevClinits = new HashMap<ClassSymbol, Symbol>();
1155 if (tree.sym.owner.kind == MTH) {
1156 localClassDefs.put(tree.sym, tree);
1157 }
1158 if (directlyEnclosingLambda() != null) {
1159 tree.sym.owner = owner();
1160 if (tree.sym.hasOuterInstance()) {
1161 //if a class is defined within a lambda, the lambda must capture
1162 //its enclosing instance (if any)
1163 TranslationContext<?> localContext = context();
1164 while (localContext != null) {
1165 if (localContext.tree.getTag() == LAMBDA) {
1166 ((LambdaTranslationContext)localContext)
1167 .addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS);
1168 }
1169 localContext = localContext.prev;
1170 }
1171 }
1172 }
1173 frameStack = frameStack.prepend(new Frame(tree));
1174 super.visitClassDef(tree);
1175 }
1176 finally {
1177 log.useSource(prevSource.getFile());
1178 frameStack = prevStack;
1179 syntheticMethodNameCounts = prevSyntheticMethodNameCounts;
1180 clinits = prevClinits;
1181 }
1182 }
1184 @Override
1185 public void visitIdent(JCIdent tree) {
1186 if (context() != null && lambdaIdentSymbolFilter(tree.sym)) {
1187 if (tree.sym.kind == VAR &&
1188 tree.sym.owner.kind == MTH &&
1189 tree.type.constValue() == null) {
1190 TranslationContext<?> localContext = context();
1191 while (localContext != null) {
1192 if (localContext.tree.getTag() == LAMBDA) {
1193 JCTree block = capturedDecl(localContext.depth, tree.sym);
1194 if (block == null) break;
1195 ((LambdaTranslationContext)localContext)
1196 .addSymbol(tree.sym, CAPTURED_VAR);
1197 }
1198 localContext = localContext.prev;
1199 }
1200 } else if (tree.sym.owner.kind == TYP) {
1201 TranslationContext<?> localContext = context();
1202 while (localContext != null) {
1203 if (localContext.tree.hasTag(LAMBDA)) {
1204 JCTree block = capturedDecl(localContext.depth, tree.sym);
1205 if (block == null) break;
1206 switch (block.getTag()) {
1207 case CLASSDEF:
1208 JCClassDecl cdecl = (JCClassDecl)block;
1209 ((LambdaTranslationContext)localContext)
1210 .addSymbol(cdecl.sym, CAPTURED_THIS);
1211 break;
1212 default:
1213 Assert.error("bad block kind");
1214 }
1215 }
1216 localContext = localContext.prev;
1217 }
1218 }
1219 }
1220 super.visitIdent(tree);
1221 }
1223 @Override
1224 public void visitLambda(JCLambda tree) {
1225 analyzeLambda(tree, "lambda.stat");
1226 }
1228 private void analyzeLambda(JCLambda tree, JCExpression methodReferenceReceiver) {
1229 // Translation of the receiver expression must occur first
1230 JCExpression rcvr = translate(methodReferenceReceiver);
1231 LambdaTranslationContext context = analyzeLambda(tree, "mref.stat.1");
1232 if (rcvr != null) {
1233 context.methodReferenceReceiver = rcvr;
1234 }
1235 }
1237 private LambdaTranslationContext analyzeLambda(JCLambda tree, String statKey) {
1238 List<Frame> prevStack = frameStack;
1239 try {
1240 LambdaTranslationContext context = new LambdaTranslationContext(tree);
1241 if (dumpLambdaToMethodStats) {
1242 log.note(tree, statKey, context.needsAltMetafactory(), context.translatedSym);
1243 }
1244 frameStack = frameStack.prepend(new Frame(tree));
1245 for (JCVariableDecl param : tree.params) {
1246 context.addSymbol(param.sym, PARAM);
1247 frameStack.head.addLocal(param.sym);
1248 }
1249 contextMap.put(tree, context);
1250 super.visitLambda(tree);
1251 context.complete();
1252 return context;
1253 }
1254 finally {
1255 frameStack = prevStack;
1256 }
1257 }
1259 @Override
1260 public void visitMethodDef(JCMethodDecl tree) {
1261 List<Frame> prevStack = frameStack;
1262 try {
1263 frameStack = frameStack.prepend(new Frame(tree));
1264 super.visitMethodDef(tree);
1265 }
1266 finally {
1267 frameStack = prevStack;
1268 }
1269 }
1271 @Override
1272 public void visitNewClass(JCNewClass tree) {
1273 TypeSymbol def = tree.type.tsym;
1274 boolean inReferencedClass = currentlyInClass(def);
1275 boolean isLocal = def.isLocal();
1276 if ((inReferencedClass && isLocal || lambdaNewClassFilter(context(), tree))) {
1277 TranslationContext<?> localContext = context();
1278 while (localContext != null) {
1279 if (localContext.tree.getTag() == LAMBDA) {
1280 ((LambdaTranslationContext)localContext)
1281 .addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS);
1282 }
1283 localContext = localContext.prev;
1284 }
1285 }
1286 if (context() != null && !inReferencedClass && isLocal) {
1287 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context();
1288 captureLocalClassDefs(def, lambdaContext);
1289 }
1290 super.visitNewClass(tree);
1291 }
1292 //where
1293 void captureLocalClassDefs(Symbol csym, final LambdaTranslationContext lambdaContext) {
1294 JCClassDecl localCDef = localClassDefs.get(csym);
1295 if (localCDef != null && lambdaContext.freeVarProcessedLocalClasses.add(csym)) {
1296 BasicFreeVarCollector fvc = lower.new BasicFreeVarCollector() {
1297 @Override
1298 void addFreeVars(ClassSymbol c) {
1299 captureLocalClassDefs(c, lambdaContext);
1300 }
1301 @Override
1302 void visitSymbol(Symbol sym) {
1303 if (sym.kind == VAR &&
1304 sym.owner.kind == MTH &&
1305 ((VarSymbol)sym).getConstValue() == null) {
1306 TranslationContext<?> localContext = context();
1307 while (localContext != null) {
1308 if (localContext.tree.getTag() == LAMBDA) {
1309 JCTree block = capturedDecl(localContext.depth, sym);
1310 if (block == null) break;
1311 ((LambdaTranslationContext)localContext).addSymbol(sym, CAPTURED_VAR);
1312 }
1313 localContext = localContext.prev;
1314 }
1315 }
1316 }
1317 };
1318 fvc.scan(localCDef);
1319 }
1320 }
1321 //where
1322 boolean currentlyInClass(Symbol csym) {
1323 for (Frame frame : frameStack) {
1324 if (frame.tree.hasTag(JCTree.Tag.CLASSDEF)) {
1325 JCClassDecl cdef = (JCClassDecl) frame.tree;
1326 if (cdef.sym == csym) {
1327 return true;
1328 }
1329 }
1330 }
1331 return false;
1332 }
1334 /**
1335 * Method references to local class constructors, may, if the local
1336 * class references local variables, have implicit constructor
1337 * parameters added in Lower; As a result, the invokedynamic bootstrap
1338 * information added in the LambdaToMethod pass will have the wrong
1339 * signature. Hooks between Lower and LambdaToMethod have been added to
1340 * handle normal "new" in this case. This visitor converts potentially
1341 * affected method references into a lambda containing a normal
1342 * expression.
1343 *
1344 * @param tree
1345 */
1346 @Override
1347 public void visitReference(JCMemberReference tree) {
1348 ReferenceTranslationContext rcontext = new ReferenceTranslationContext(tree);
1349 contextMap.put(tree, rcontext);
1350 if (rcontext.needsConversionToLambda()) {
1351 // Convert to a lambda, and process as such
1352 MemberReferenceToLambda conv = new MemberReferenceToLambda(tree, rcontext, owner());
1353 analyzeLambda(conv.lambda(), conv.getReceiverExpression());
1354 } else {
1355 super.visitReference(tree);
1356 if (dumpLambdaToMethodStats) {
1357 log.note(tree, "mref.stat", rcontext.needsAltMetafactory(), null);
1358 }
1359 }
1360 }
1362 @Override
1363 public void visitSelect(JCFieldAccess tree) {
1364 if (context() != null && tree.sym.kind == VAR &&
1365 (tree.sym.name == names._this ||
1366 tree.sym.name == names._super)) {
1367 // A select of this or super means, if we are in a lambda,
1368 // we much have an instance context
1369 TranslationContext<?> localContext = context();
1370 while (localContext != null) {
1371 if (localContext.tree.hasTag(LAMBDA)) {
1372 JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym);
1373 if (clazz == null) break;
1374 ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS);
1375 }
1376 localContext = localContext.prev;
1377 }
1378 }
1379 super.visitSelect(tree);
1380 }
1382 @Override
1383 public void visitVarDef(JCVariableDecl tree) {
1384 TranslationContext<?> context = context();
1385 LambdaTranslationContext ltc = (context != null && context instanceof LambdaTranslationContext)?
1386 (LambdaTranslationContext)context :
1387 null;
1388 if (ltc != null) {
1389 if (frameStack.head.tree.hasTag(LAMBDA)) {
1390 ltc.addSymbol(tree.sym, LOCAL_VAR);
1391 }
1392 // Check for type variables (including as type arguments).
1393 // If they occur within class nested in a lambda, mark for erasure
1394 Type type = tree.sym.asType();
1395 if (inClassWithinLambda() && !types.isSameType(types.erasure(type), type)) {
1396 ltc.addSymbol(tree.sym, TYPE_VAR);
1397 }
1398 }
1400 List<Frame> prevStack = frameStack;
1401 try {
1402 if (tree.sym.owner.kind == MTH) {
1403 frameStack.head.addLocal(tree.sym);
1404 }
1405 frameStack = frameStack.prepend(new Frame(tree));
1406 super.visitVarDef(tree);
1407 }
1408 finally {
1409 frameStack = prevStack;
1410 }
1411 }
1413 /**
1414 * Return a valid owner given the current declaration stack
1415 * (required to skip synthetic lambda symbols)
1416 */
1417 private Symbol owner() {
1418 return owner(false);
1419 }
1421 @SuppressWarnings("fallthrough")
1422 private Symbol owner(boolean skipLambda) {
1423 List<Frame> frameStack2 = frameStack;
1424 while (frameStack2.nonEmpty()) {
1425 switch (frameStack2.head.tree.getTag()) {
1426 case VARDEF:
1427 if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) {
1428 frameStack2 = frameStack2.tail;
1429 break;
1430 }
1431 JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree;
1432 return initSym(cdecl.sym,
1433 ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC);
1434 case BLOCK:
1435 JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree;
1436 return initSym(cdecl2.sym,
1437 ((JCBlock)frameStack2.head.tree).flags & STATIC);
1438 case CLASSDEF:
1439 return ((JCClassDecl)frameStack2.head.tree).sym;
1440 case METHODDEF:
1441 return ((JCMethodDecl)frameStack2.head.tree).sym;
1442 case LAMBDA:
1443 if (!skipLambda)
1444 return ((LambdaTranslationContext)contextMap
1445 .get(frameStack2.head.tree)).translatedSym;
1446 default:
1447 frameStack2 = frameStack2.tail;
1448 }
1449 }
1450 Assert.error();
1451 return null;
1452 }
1454 private Symbol initSym(ClassSymbol csym, long flags) {
1455 boolean isStatic = (flags & STATIC) != 0;
1456 if (isStatic) {
1457 /* static clinits are generated in Gen, so we need to use a fake
1458 * one. Attr creates a fake clinit method while attributing
1459 * lambda expressions used as initializers of static fields, so
1460 * let's use that one.
1461 */
1462 MethodSymbol clinit = attr.removeClinit(csym);
1463 if (clinit != null) {
1464 clinits.put(csym, clinit);
1465 return clinit;
1466 }
1468 /* if no clinit is found at Attr, then let's try at clinits.
1469 */
1470 clinit = (MethodSymbol)clinits.get(csym);
1471 if (clinit == null) {
1472 /* no luck, let's create a new one
1473 */
1474 clinit = makePrivateSyntheticMethod(STATIC,
1475 names.clinit,
1476 new MethodType(List.<Type>nil(), syms.voidType,
1477 List.<Type>nil(), syms.methodClass),
1478 csym);
1479 clinits.put(csym, clinit);
1480 }
1481 return clinit;
1482 } else {
1483 //get the first constructor and treat it as the instance init sym
1484 for (Symbol s : csym.members_field.getElementsByName(names.init)) {
1485 return s;
1486 }
1487 }
1488 Assert.error("init not found");
1489 return null;
1490 }
1492 private JCTree directlyEnclosingLambda() {
1493 if (frameStack.isEmpty()) {
1494 return null;
1495 }
1496 List<Frame> frameStack2 = frameStack;
1497 while (frameStack2.nonEmpty()) {
1498 switch (frameStack2.head.tree.getTag()) {
1499 case CLASSDEF:
1500 case METHODDEF:
1501 return null;
1502 case LAMBDA:
1503 return frameStack2.head.tree;
1504 default:
1505 frameStack2 = frameStack2.tail;
1506 }
1507 }
1508 Assert.error();
1509 return null;
1510 }
1512 private boolean inClassWithinLambda() {
1513 if (frameStack.isEmpty()) {
1514 return false;
1515 }
1516 List<Frame> frameStack2 = frameStack;
1517 boolean classFound = false;
1518 while (frameStack2.nonEmpty()) {
1519 switch (frameStack2.head.tree.getTag()) {
1520 case LAMBDA:
1521 return classFound;
1522 case CLASSDEF:
1523 classFound = true;
1524 frameStack2 = frameStack2.tail;
1525 break;
1526 default:
1527 frameStack2 = frameStack2.tail;
1528 }
1529 }
1530 // No lambda
1531 return false;
1532 }
1534 /**
1535 * Return the declaration corresponding to a symbol in the enclosing
1536 * scope; the depth parameter is used to filter out symbols defined
1537 * in nested scopes (which do not need to undergo capture).
1538 */
1539 private JCTree capturedDecl(int depth, Symbol sym) {
1540 int currentDepth = frameStack.size() - 1;
1541 for (Frame block : frameStack) {
1542 switch (block.tree.getTag()) {
1543 case CLASSDEF:
1544 ClassSymbol clazz = ((JCClassDecl)block.tree).sym;
1545 if (sym.isMemberOf(clazz, types)) {
1546 return currentDepth > depth ? null : block.tree;
1547 }
1548 break;
1549 case VARDEF:
1550 if (((JCVariableDecl)block.tree).sym == sym &&
1551 sym.owner.kind == MTH) { //only locals are captured
1552 return currentDepth > depth ? null : block.tree;
1553 }
1554 break;
1555 case BLOCK:
1556 case METHODDEF:
1557 case LAMBDA:
1558 if (block.locals != null && block.locals.contains(sym)) {
1559 return currentDepth > depth ? null : block.tree;
1560 }
1561 break;
1562 default:
1563 Assert.error("bad decl kind " + block.tree.getTag());
1564 }
1565 currentDepth--;
1566 }
1567 return null;
1568 }
1570 private TranslationContext<?> context() {
1571 for (Frame frame : frameStack) {
1572 TranslationContext<?> context = contextMap.get(frame.tree);
1573 if (context != null) {
1574 return context;
1575 }
1576 }
1577 return null;
1578 }
1580 /**
1581 * This is used to filter out those identifiers that needs to be adjusted
1582 * when translating away lambda expressions
1583 */
1584 private boolean lambdaIdentSymbolFilter(Symbol sym) {
1585 return (sym.kind == VAR || sym.kind == MTH)
1586 && !sym.isStatic()
1587 && sym.name != names.init;
1588 }
1590 /**
1591 * This is used to filter out those new class expressions that need to
1592 * be qualified with an enclosing tree
1593 */
1594 private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) {
1595 if (context != null
1596 && tree.encl == null
1597 && tree.def == null
1598 && !tree.type.getEnclosingType().hasTag(NONE)) {
1599 Type encl = tree.type.getEnclosingType();
1600 Type current = context.owner.enclClass().type;
1601 while (!current.hasTag(NONE)) {
1602 if (current.tsym.isSubClass(encl.tsym, types)) {
1603 return true;
1604 }
1605 current = current.getEnclosingType();
1606 }
1607 return false;
1608 } else {
1609 return false;
1610 }
1611 }
1613 private class Frame {
1614 final JCTree tree;
1615 List<Symbol> locals;
1617 public Frame(JCTree tree) {
1618 this.tree = tree;
1619 }
1621 void addLocal(Symbol sym) {
1622 if (locals == null) {
1623 locals = List.nil();
1624 }
1625 locals = locals.prepend(sym);
1626 }
1627 }
1629 /**
1630 * This class is used to store important information regarding translation of
1631 * lambda expression/method references (see subclasses).
1632 */
1633 private abstract class TranslationContext<T extends JCFunctionalExpression> {
1635 /** the underlying (untranslated) tree */
1636 final T tree;
1638 /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */
1639 final Symbol owner;
1641 /** the depth of this lambda expression in the frame stack */
1642 final int depth;
1644 /** the enclosing translation context (set for nested lambdas/mref) */
1645 final TranslationContext<?> prev;
1647 /** list of methods to be bridged by the meta-factory */
1648 final List<Symbol> bridges;
1650 TranslationContext(T tree) {
1651 this.tree = tree;
1652 this.owner = owner();
1653 this.depth = frameStack.size() - 1;
1654 this.prev = context();
1655 ClassSymbol csym =
1656 types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.targets, ABSTRACT | INTERFACE);
1657 this.bridges = types.functionalInterfaceBridges(csym);
1658 }
1660 /** does this functional expression need to be created using alternate metafactory? */
1661 boolean needsAltMetafactory() {
1662 return tree.targets.length() > 1 ||
1663 isSerializable() ||
1664 bridges.length() > 1;
1665 }
1667 /** does this functional expression require serialization support? */
1668 boolean isSerializable() {
1669 if (forceSerializable) {
1670 return true;
1671 }
1672 for (Type target : tree.targets) {
1673 if (types.asSuper(target, syms.serializableType.tsym) != null) {
1674 return true;
1675 }
1676 }
1677 return false;
1678 }
1680 /**
1681 * @return Name of the enclosing method to be folded into synthetic
1682 * method name
1683 */
1684 String enclosingMethodName() {
1685 return syntheticMethodNameComponent(owner.name);
1686 }
1688 /**
1689 * @return Method name in a form that can be folded into a
1690 * component of a synthetic method name
1691 */
1692 String syntheticMethodNameComponent(Name name) {
1693 if (name == null) {
1694 return "null";
1695 }
1696 String methodName = name.toString();
1697 if (methodName.equals("<clinit>")) {
1698 methodName = "static";
1699 } else if (methodName.equals("<init>")) {
1700 methodName = "new";
1701 }
1702 return methodName;
1703 }
1704 }
1706 /**
1707 * This class retains all the useful information about a lambda expression;
1708 * the contents of this class are filled by the LambdaAnalyzer visitor,
1709 * and the used by the main translation routines in order to adjust references
1710 * to captured locals/members, etc.
1711 */
1712 private class LambdaTranslationContext extends TranslationContext<JCLambda> {
1714 /** variable in the enclosing context to which this lambda is assigned */
1715 final Symbol self;
1717 /** variable in the enclosing context to which this lambda is assigned */
1718 final Symbol assignedTo;
1720 Map<LambdaSymbolKind, Map<Symbol, Symbol>> translatedSymbols;
1722 /** the synthetic symbol for the method hoisting the translated lambda */
1723 Symbol translatedSym;
1725 List<JCVariableDecl> syntheticParams;
1727 /**
1728 * to prevent recursion, track local classes processed
1729 */
1730 final Set<Symbol> freeVarProcessedLocalClasses;
1732 /**
1733 * For method references converted to lambdas. The method
1734 * reference receiver expression. Must be treated like a captured
1735 * variable.
1736 */
1737 JCExpression methodReferenceReceiver;
1739 LambdaTranslationContext(JCLambda tree) {
1740 super(tree);
1741 Frame frame = frameStack.head;
1742 switch (frame.tree.getTag()) {
1743 case VARDEF:
1744 assignedTo = self = ((JCVariableDecl) frame.tree).sym;
1745 break;
1746 case ASSIGN:
1747 self = null;
1748 assignedTo = TreeInfo.symbol(((JCAssign) frame.tree).getVariable());
1749 break;
1750 default:
1751 assignedTo = self = null;
1752 break;
1753 }
1755 // This symbol will be filled-in in complete
1756 this.translatedSym = makePrivateSyntheticMethod(0, null, null, owner.enclClass());
1758 translatedSymbols = new EnumMap<>(LambdaSymbolKind.class);
1760 translatedSymbols.put(PARAM, new LinkedHashMap<Symbol, Symbol>());
1761 translatedSymbols.put(LOCAL_VAR, new LinkedHashMap<Symbol, Symbol>());
1762 translatedSymbols.put(CAPTURED_VAR, new LinkedHashMap<Symbol, Symbol>());
1763 translatedSymbols.put(CAPTURED_THIS, new LinkedHashMap<Symbol, Symbol>());
1764 translatedSymbols.put(TYPE_VAR, new LinkedHashMap<Symbol, Symbol>());
1766 freeVarProcessedLocalClasses = new HashSet<>();
1767 }
1769 /**
1770 * For a serializable lambda, generate a disambiguating string
1771 * which maximizes stability across deserialization.
1772 *
1773 * @return String to differentiate synthetic lambda method names
1774 */
1775 private String serializedLambdaDisambiguation() {
1776 StringBuilder buf = new StringBuilder();
1777 // Append the enclosing method signature to differentiate
1778 // overloaded enclosing methods. For lambdas enclosed in
1779 // lambdas, the generated lambda method will not have type yet,
1780 // but the enclosing method's name will have been generated
1781 // with this same method, so it will be unique and never be
1782 // overloaded.
1783 Assert.check(
1784 owner.type != null ||
1785 directlyEnclosingLambda() != null);
1786 if (owner.type != null) {
1787 buf.append(typeSig(owner.type));
1788 buf.append(":");
1789 }
1791 // Add target type info
1792 buf.append(types.findDescriptorSymbol(tree.type.tsym).owner.flatName());
1793 buf.append(" ");
1795 // Add variable assigned to
1796 if (assignedTo != null) {
1797 buf.append(assignedTo.flatName());
1798 buf.append("=");
1799 }
1800 //add captured locals info: type, name, order
1801 for (Symbol fv : getSymbolMap(CAPTURED_VAR).keySet()) {
1802 if (fv != self) {
1803 buf.append(typeSig(fv.type));
1804 buf.append(" ");
1805 buf.append(fv.flatName());
1806 buf.append(",");
1807 }
1808 }
1810 return buf.toString();
1811 }
1813 /**
1814 * For a non-serializable lambda, generate a simple method.
1815 *
1816 * @return Name to use for the synthetic lambda method name
1817 */
1818 private Name lambdaName() {
1819 return names.lambda.append(names.fromString(enclosingMethodName() + "$" + lambdaCount++));
1820 }
1822 /**
1823 * For a serializable lambda, generate a method name which maximizes
1824 * name stability across deserialization.
1825 *
1826 * @return Name to use for the synthetic lambda method name
1827 */
1828 private Name serializedLambdaName() {
1829 StringBuilder buf = new StringBuilder();
1830 buf.append(names.lambda);
1831 // Append the name of the method enclosing the lambda.
1832 buf.append(enclosingMethodName());
1833 buf.append('$');
1834 // Append a hash of the disambiguating string : enclosing method
1835 // signature, etc.
1836 String disam = serializedLambdaDisambiguation();
1837 buf.append(Integer.toHexString(disam.hashCode()));
1838 buf.append('$');
1839 // The above appended name components may not be unique, append
1840 // a count based on the above name components.
1841 buf.append(syntheticMethodNameCounts.getIndex(buf));
1842 String result = buf.toString();
1843 //System.err.printf("serializedLambdaName: %s -- %s\n", result, disam);
1844 return names.fromString(result);
1845 }
1847 /**
1848 * Translate a symbol of a given kind into something suitable for the
1849 * synthetic lambda body
1850 */
1851 Symbol translate(Name name, final Symbol sym, LambdaSymbolKind skind) {
1852 Symbol ret;
1853 switch (skind) {
1854 case CAPTURED_THIS:
1855 ret = sym; // self represented
1856 break;
1857 case TYPE_VAR:
1858 // Just erase the type var
1859 ret = new VarSymbol(sym.flags(), name,
1860 types.erasure(sym.type), sym.owner);
1862 /* this information should also be kept for LVT generation at Gen
1863 * a Symbol with pos < startPos won't be tracked.
1864 */
1865 ((VarSymbol)ret).pos = ((VarSymbol)sym).pos;
1866 break;
1867 case CAPTURED_VAR:
1868 ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym) {
1869 @Override
1870 public Symbol baseSymbol() {
1871 //keep mapping with original captured symbol
1872 return sym;
1873 }
1874 };
1875 break;
1876 case LOCAL_VAR:
1877 ret = new VarSymbol(sym.flags() & FINAL, name, sym.type, translatedSym);
1878 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
1879 break;
1880 case PARAM:
1881 ret = new VarSymbol((sym.flags() & FINAL) | PARAMETER, name, types.erasure(sym.type), translatedSym);
1882 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
1883 break;
1884 default:
1885 ret = makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
1886 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
1887 }
1888 if (ret != sym) {
1889 ret.setDeclarationAttributes(sym.getRawAttributes());
1890 ret.setTypeAttributes(sym.getRawTypeAttributes());
1891 }
1892 return ret;
1893 }
1895 void addSymbol(Symbol sym, LambdaSymbolKind skind) {
1896 Map<Symbol, Symbol> transMap = getSymbolMap(skind);
1897 Name preferredName;
1898 switch (skind) {
1899 case CAPTURED_THIS:
1900 preferredName = names.fromString("encl$" + transMap.size());
1901 break;
1902 case CAPTURED_VAR:
1903 preferredName = names.fromString("cap$" + transMap.size());
1904 break;
1905 case LOCAL_VAR:
1906 preferredName = sym.name;
1907 break;
1908 case PARAM:
1909 preferredName = sym.name;
1910 break;
1911 case TYPE_VAR:
1912 preferredName = sym.name;
1913 break;
1914 default: throw new AssertionError();
1915 }
1916 if (!transMap.containsKey(sym)) {
1917 transMap.put(sym, translate(preferredName, sym, skind));
1918 }
1919 }
1921 Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind skind) {
1922 Map<Symbol, Symbol> m = translatedSymbols.get(skind);
1923 Assert.checkNonNull(m);
1924 return m;
1925 }
1927 JCTree translate(JCIdent lambdaIdent) {
1928 for (Map<Symbol, Symbol> m : translatedSymbols.values()) {
1929 if (m.containsKey(lambdaIdent.sym)) {
1930 Symbol tSym = m.get(lambdaIdent.sym);
1931 JCTree t = make.Ident(tSym).setType(lambdaIdent.type);
1932 tSym.setTypeAttributes(lambdaIdent.sym.getRawTypeAttributes());
1933 return t;
1934 }
1935 }
1936 return null;
1937 }
1939 /**
1940 * The translatedSym is not complete/accurate until the analysis is
1941 * finished. Once the analysis is finished, the translatedSym is
1942 * "completed" -- updated with type information, access modifiers,
1943 * and full parameter list.
1944 */
1945 void complete() {
1946 if (syntheticParams != null) {
1947 return;
1948 }
1949 boolean inInterface = translatedSym.owner.isInterface();
1950 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty();
1952 // If instance access isn't needed, make it static.
1953 // Interface instance methods must be default methods.
1954 // Lambda methods are private synthetic.
1955 // Inherit ACC_STRICT from the enclosing method, or, for clinit,
1956 // from the class.
1957 translatedSym.flags_field = SYNTHETIC | LAMBDA_METHOD |
1958 owner.flags_field & STRICTFP |
1959 owner.owner.flags_field & STRICTFP |
1960 PRIVATE |
1961 (thisReferenced? (inInterface? DEFAULT : 0) : STATIC);
1963 //compute synthetic params
1964 ListBuffer<JCVariableDecl> params = new ListBuffer<>();
1966 // The signature of the method is augmented with the following
1967 // synthetic parameters:
1968 //
1969 // 1) reference to enclosing contexts captured by the lambda expression
1970 // 2) enclosing locals captured by the lambda expression
1971 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR).values()) {
1972 params.append(make.VarDef((VarSymbol) thisSym, null));
1973 }
1974 if (methodReferenceReceiver != null) {
1975 params.append(make.VarDef(
1976 make.Modifiers(PARAMETER|FINAL),
1977 names.fromString("$rcvr$"),
1978 make.Type(methodReferenceReceiver.type),
1979 null));
1980 }
1981 for (Symbol thisSym : getSymbolMap(PARAM).values()) {
1982 params.append(make.VarDef((VarSymbol) thisSym, null));
1983 }
1984 syntheticParams = params.toList();
1986 // Compute and set the lambda name
1987 translatedSym.name = isSerializable()
1988 ? serializedLambdaName()
1989 : lambdaName();
1991 //prepend synthetic args to translated lambda method signature
1992 translatedSym.type = types.createMethodTypeWithParameters(
1993 generatedLambdaSig(),
1994 TreeInfo.types(syntheticParams));
1995 }
1997 Type generatedLambdaSig() {
1998 return types.erasure(tree.getDescriptorType(types));
1999 }
2000 }
2002 /**
2003 * This class retains all the useful information about a method reference;
2004 * the contents of this class are filled by the LambdaAnalyzer visitor,
2005 * and the used by the main translation routines in order to adjust method
2006 * references (i.e. in case a bridge is needed)
2007 */
2008 private final class ReferenceTranslationContext extends TranslationContext<JCMemberReference> {
2010 final boolean isSuper;
2011 final Symbol sigPolySym;
2013 ReferenceTranslationContext(JCMemberReference tree) {
2014 super(tree);
2015 this.isSuper = tree.hasKind(ReferenceKind.SUPER);
2016 this.sigPolySym = isSignaturePolymorphic()
2017 ? makePrivateSyntheticMethod(tree.sym.flags(),
2018 tree.sym.name,
2019 bridgedRefSig(),
2020 tree.sym.enclClass())
2021 : null;
2022 }
2024 /**
2025 * Get the opcode associated with this method reference
2026 */
2027 int referenceKind() {
2028 return LambdaToMethod.this.referenceKind(tree.sym);
2029 }
2031 boolean needsVarArgsConversion() {
2032 return tree.varargsElement != null;
2033 }
2035 /**
2036 * @return Is this an array operation like clone()
2037 */
2038 boolean isArrayOp() {
2039 return tree.sym.owner == syms.arrayClass;
2040 }
2042 boolean receiverAccessible() {
2043 //hack needed to workaround 292 bug (7087658)
2044 //when 292 issue is fixed we should remove this and change the backend
2045 //code to always generate a method handle to an accessible method
2046 return tree.ownerAccessible;
2047 }
2049 /**
2050 * The VM does not support access across nested classes (8010319).
2051 * Were that ever to change, this should be removed.
2052 */
2053 boolean isPrivateInOtherClass() {
2054 return (tree.sym.flags() & PRIVATE) != 0 &&
2055 !types.isSameType(
2056 types.erasure(tree.sym.enclClass().asType()),
2057 types.erasure(owner.enclClass().asType()));
2058 }
2060 /**
2061 * Signature polymorphic methods need special handling.
2062 * e.g. MethodHandle.invoke() MethodHandle.invokeExact()
2063 */
2064 final boolean isSignaturePolymorphic() {
2065 return tree.sym.kind == MTH &&
2066 types.isSignaturePolymorphic((MethodSymbol)tree.sym);
2067 }
2069 /**
2070 * Does this reference need to be converted to a lambda
2071 * (i.e. var args need to be expanded or "super" is used)
2072 */
2073 final boolean needsConversionToLambda() {
2074 return isSuper || needsVarArgsConversion() || isArrayOp() ||
2075 isPrivateInOtherClass() ||
2076 !receiverAccessible() ||
2077 (tree.getMode() == ReferenceMode.NEW &&
2078 tree.kind != ReferenceKind.ARRAY_CTOR &&
2079 (tree.sym.owner.isLocal() || tree.sym.owner.isInner()));
2080 }
2082 Type generatedRefSig() {
2083 return types.erasure(tree.sym.type);
2084 }
2086 Type bridgedRefSig() {
2087 return types.erasure(types.findDescriptorSymbol(tree.targets.head.tsym).type);
2088 }
2089 }
2090 }
2091 // </editor-fold>
2093 /*
2094 * These keys provide mappings for various translated lambda symbols
2095 * and the prevailing order must be maintained.
2096 */
2097 enum LambdaSymbolKind {
2098 PARAM, // original to translated lambda parameters
2099 LOCAL_VAR, // original to translated lambda locals
2100 CAPTURED_VAR, // variables in enclosing scope to translated synthetic parameters
2101 CAPTURED_THIS, // class symbols to translated synthetic parameters (for captured member access)
2102 TYPE_VAR; // original to translated lambda type variables
2103 }
2105 /**
2106 * ****************************************************************
2107 * Signature Generation
2108 * ****************************************************************
2109 */
2111 private String typeSig(Type type) {
2112 L2MSignatureGenerator sg = new L2MSignatureGenerator();
2113 sg.assembleSig(type);
2114 return sg.toString();
2115 }
2117 private String classSig(Type type) {
2118 L2MSignatureGenerator sg = new L2MSignatureGenerator();
2119 sg.assembleClassSig(type);
2120 return sg.toString();
2121 }
2123 /**
2124 * Signature Generation
2125 */
2126 private class L2MSignatureGenerator extends Types.SignatureGenerator {
2128 /**
2129 * An output buffer for type signatures.
2130 */
2131 StringBuilder sb = new StringBuilder();
2133 L2MSignatureGenerator() {
2134 super(types);
2135 }
2137 @Override
2138 protected void append(char ch) {
2139 sb.append(ch);
2140 }
2142 @Override
2143 protected void append(byte[] ba) {
2144 sb.append(new String(ba));
2145 }
2147 @Override
2148 protected void append(Name name) {
2149 sb.append(name.toString());
2150 }
2152 @Override
2153 public String toString() {
2154 return sb.toString();
2155 }
2156 }
2157 }