Wed, 18 Jun 2014 12:06:50 -0400
8038975: Access control in enhanced for
Reviewed-by: vromero, jlahoda
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 (!sym.isStatic()) {
325 syntheticInits.append(makeThis(
326 sym.owner.enclClass().asType(),
327 localContext.owner.enclClass()));
328 }
330 //add captured locals
331 for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) {
332 if (fv != localContext.self) {
333 JCTree captured_local = make.Ident(fv).setType(fv.type);
334 syntheticInits.append((JCExpression) captured_local);
335 }
336 }
338 //then, determine the arguments to the indy call
339 List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev);
341 //build a sam instance using an indy call to the meta-factory
342 int refKind = referenceKind(sym);
344 //convert to an invokedynamic call
345 result = makeMetafactoryIndyCall(context, refKind, sym, indy_args);
346 }
348 private JCIdent makeThis(Type type, Symbol owner) {
349 VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC,
350 names._this,
351 type,
352 owner);
353 return make.Ident(_this);
354 }
356 /**
357 * Translate a method reference into an invokedynamic call to the
358 * meta-factory.
359 * @param tree
360 */
361 @Override
362 public void visitReference(JCMemberReference tree) {
363 ReferenceTranslationContext localContext = (ReferenceTranslationContext)context;
365 //first determine the method symbol to be used to generate the sam instance
366 //this is either the method reference symbol, or the bridged reference symbol
367 Symbol refSym = localContext.needsBridge()
368 ? localContext.bridgeSym
369 : localContext.isSignaturePolymorphic()
370 ? localContext.sigPolySym
371 : tree.sym;
373 //build the bridge method, if needed
374 if (localContext.needsBridge()) {
375 bridgeMemberReference(tree, localContext);
376 }
378 //the qualifying expression is treated as a special captured arg
379 JCExpression init;
380 switch(tree.kind) {
382 case IMPLICIT_INNER: /** Inner :: new */
383 case SUPER: /** super :: instMethod */
384 init = makeThis(
385 localContext.owner.enclClass().asType(),
386 localContext.owner.enclClass());
387 break;
389 case BOUND: /** Expr :: instMethod */
390 init = tree.getQualifierExpression();
391 init = attr.makeNullCheck(init);
392 break;
394 case UNBOUND: /** Type :: instMethod */
395 case STATIC: /** Type :: staticMethod */
396 case TOPLEVEL: /** Top level :: new */
397 case ARRAY_CTOR: /** ArrayType :: new */
398 init = null;
399 break;
401 default:
402 throw new InternalError("Should not have an invalid kind");
403 }
405 List<JCExpression> indy_args = init==null? List.<JCExpression>nil() : translate(List.of(init), localContext.prev);
408 //build a sam instance using an indy call to the meta-factory
409 result = makeMetafactoryIndyCall(localContext, localContext.referenceKind(), refSym, indy_args);
410 }
412 /**
413 * Translate identifiers within a lambda to the mapped identifier
414 * @param tree
415 */
416 @Override
417 public void visitIdent(JCIdent tree) {
418 if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) {
419 super.visitIdent(tree);
420 } else {
421 int prevPos = make.pos;
422 try {
423 make.at(tree);
425 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context;
426 JCTree ltree = lambdaContext.translate(tree);
427 if (ltree != null) {
428 result = ltree;
429 } else {
430 //access to untranslated symbols (i.e. compile-time constants,
431 //members defined inside the lambda body, etc.) )
432 super.visitIdent(tree);
433 }
434 } finally {
435 make.at(prevPos);
436 }
437 }
438 }
440 @Override
441 public void visitVarDef(JCVariableDecl tree) {
442 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context;
443 if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
444 tree.init = translate(tree.init);
445 tree.sym = (VarSymbol) lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym);
446 result = tree;
447 } else if (context != null && lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
448 JCExpression init = translate(tree.init);
449 VarSymbol xsym = (VarSymbol)lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
450 int prevPos = make.pos;
451 try {
452 result = make.at(tree).VarDef(xsym, init);
453 } finally {
454 make.at(prevPos);
455 }
456 // Replace the entered symbol for this variable
457 Scope sc = tree.sym.owner.members();
458 if (sc != null) {
459 sc.remove(tree.sym);
460 sc.enter(xsym);
461 }
462 } else {
463 super.visitVarDef(tree);
464 }
465 }
467 // </editor-fold>
469 // <editor-fold defaultstate="collapsed" desc="Translation helper methods">
471 private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) {
472 return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ?
473 makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) :
474 makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally);
475 }
477 private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) {
478 Type restype = lambdaMethodDecl.type.getReturnType();
479 boolean isLambda_void = expr.type.hasTag(VOID);
480 boolean isTarget_void = restype.hasTag(VOID);
481 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
482 int prevPos = make.pos;
483 try {
484 if (isTarget_void) {
485 //target is void:
486 // BODY;
487 JCStatement stat = make.at(expr).Exec(expr);
488 return make.Block(0, List.<JCStatement>of(stat));
489 } else if (isLambda_void && isTarget_Void) {
490 //void to Void conversion:
491 // BODY; return null;
492 ListBuffer<JCStatement> stats = new ListBuffer<>();
493 stats.append(make.at(expr).Exec(expr));
494 stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
495 return make.Block(0, stats.toList());
496 } else {
497 //non-void to non-void conversion:
498 // return (TYPE)BODY;
499 JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype);
500 return make.at(retExpr).Block(0, List.<JCStatement>of(make.Return(retExpr)));
501 }
502 } finally {
503 make.at(prevPos);
504 }
505 }
507 private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) {
508 final Type restype = lambdaMethodDecl.type.getReturnType();
509 final boolean isTarget_void = restype.hasTag(VOID);
510 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
512 class LambdaBodyTranslator extends TreeTranslator {
514 @Override
515 public void visitClassDef(JCClassDecl tree) {
516 //do NOT recurse on any inner classes
517 result = tree;
518 }
520 @Override
521 public void visitLambda(JCLambda tree) {
522 //do NOT recurse on any nested lambdas
523 result = tree;
524 }
526 @Override
527 public void visitReturn(JCReturn tree) {
528 boolean isLambda_void = tree.expr == null;
529 if (isTarget_void && !isLambda_void) {
530 //Void to void conversion:
531 // { TYPE $loc = RET-EXPR; return; }
532 VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym);
533 JCVariableDecl varDef = make.VarDef(loc, tree.expr);
534 result = make.Block(0, List.<JCStatement>of(varDef, make.Return(null)));
535 } else if (!isTarget_void || !isLambda_void) {
536 //non-void to non-void conversion:
537 // return (TYPE)RET-EXPR;
538 tree.expr = transTypes.coerce(attrEnv, tree.expr, restype);
539 result = tree;
540 } else {
541 result = tree;
542 }
544 }
545 }
547 JCBlock trans_block = new LambdaBodyTranslator().translate(block);
548 if (completeNormally && isTarget_Void) {
549 //there's no return statement and the lambda (possibly inferred)
550 //return type is java.lang.Void; emit a synthetic return statement
551 trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
552 }
553 return trans_block;
554 }
556 private JCMethodDecl makeDeserializeMethod(Symbol kSym) {
557 ListBuffer<JCCase> cases = new ListBuffer<>();
558 ListBuffer<JCBreak> breaks = new ListBuffer<>();
559 for (Map.Entry<String, ListBuffer<JCStatement>> entry : kInfo.deserializeCases.entrySet()) {
560 JCBreak br = make.Break(null);
561 breaks.add(br);
562 List<JCStatement> stmts = entry.getValue().append(br).toList();
563 cases.add(make.Case(make.Literal(entry.getKey()), stmts));
564 }
565 JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
566 for (JCBreak br : breaks) {
567 br.target = sw;
568 }
569 JCBlock body = make.Block(0L, List.<JCStatement>of(
570 sw,
571 make.Throw(makeNewClass(
572 syms.illegalArgumentExceptionType,
573 List.<JCExpression>of(make.Literal("Invalid lambda deserialization"))))));
574 JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()),
575 names.deserializeLambda,
576 make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym),
577 List.<JCTypeParameter>nil(),
578 List.of(make.VarDef(kInfo.deserParamSym, null)),
579 List.<JCExpression>nil(),
580 body,
581 null);
582 deser.sym = kInfo.deserMethodSym;
583 deser.type = kInfo.deserMethodSym.type;
584 //System.err.printf("DESER: '%s'\n", deser);
585 return deser;
586 }
588 /** Make an attributed class instance creation expression.
589 * @param ctype The class type.
590 * @param args The constructor arguments.
591 * @param cons The constructor symbol
592 */
593 JCNewClass makeNewClass(Type ctype, List<JCExpression> args, Symbol cons) {
594 JCNewClass tree = make.NewClass(null,
595 null, make.QualIdent(ctype.tsym), args, null);
596 tree.constructor = cons;
597 tree.type = ctype;
598 return tree;
599 }
601 /** Make an attributed class instance creation expression.
602 * @param ctype The class type.
603 * @param args The constructor arguments.
604 */
605 JCNewClass makeNewClass(Type ctype, List<JCExpression> args) {
606 return makeNewClass(ctype, args,
607 rs.resolveConstructor(null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil()));
608 }
610 private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym,
611 DiagnosticPosition pos, List<Object> staticArgs, MethodType indyType) {
612 String functionalInterfaceClass = classSig(targetType);
613 String functionalInterfaceMethodName = samSym.getSimpleName().toString();
614 String functionalInterfaceMethodSignature = typeSig(types.erasure(samSym.type));
615 String implClass = classSig(types.erasure(refSym.owner.type));
616 String implMethodName = refSym.getQualifiedName().toString();
617 String implMethodSignature = typeSig(types.erasure(refSym.type));
619 JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType), make.Literal(implMethodKind));
620 ListBuffer<JCExpression> serArgs = new ListBuffer<>();
621 int i = 0;
622 for (Type t : indyType.getParameterTypes()) {
623 List<JCExpression> indexAsArg = new ListBuffer<JCExpression>().append(make.Literal(i)).toList();
624 List<Type> argTypes = new ListBuffer<Type>().append(syms.intType).toList();
625 serArgs.add(make.TypeCast(types.erasure(t), deserGetter("getCapturedArg", syms.objectType, argTypes, indexAsArg)));
626 ++i;
627 }
628 JCStatement stmt = make.If(
629 deserTest(deserTest(deserTest(deserTest(deserTest(
630 kindTest,
631 "getFunctionalInterfaceClass", functionalInterfaceClass),
632 "getFunctionalInterfaceMethodName", functionalInterfaceMethodName),
633 "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature),
634 "getImplClass", implClass),
635 "getImplMethodSignature", implMethodSignature),
636 make.Return(makeIndyCall(
637 pos,
638 syms.lambdaMetafactory,
639 names.altMetafactory,
640 staticArgs, indyType, serArgs.toList(), samSym.name)),
641 null);
642 ListBuffer<JCStatement> stmts = kInfo.deserializeCases.get(implMethodName);
643 if (stmts == null) {
644 stmts = new ListBuffer<>();
645 kInfo.deserializeCases.put(implMethodName, stmts);
646 }
647 /****
648 System.err.printf("+++++++++++++++++\n");
649 System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass);
650 System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName);
651 System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature);
652 System.err.printf("*implMethodKind: %d\n", implMethodKind);
653 System.err.printf("*implClass: '%s'\n", implClass);
654 System.err.printf("*implMethodName: '%s'\n", implMethodName);
655 System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature);
656 ****/
657 stmts.append(stmt);
658 }
660 private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) {
661 JCBinary testExpr = make.Binary(JCTree.Tag.EQ, arg1, arg2);
662 testExpr.operator = rs.resolveBinaryOperator(null, JCTree.Tag.EQ, attrEnv, argType, argType);
663 testExpr.setType(syms.booleanType);
664 return testExpr;
665 }
667 private JCExpression deserTest(JCExpression prev, String func, String lit) {
668 MethodType eqmt = new MethodType(List.of(syms.objectType), syms.booleanType, List.<Type>nil(), syms.methodClass);
669 Symbol eqsym = rs.resolveQualifiedMethod(null, attrEnv, syms.objectType, names.equals, List.of(syms.objectType), List.<Type>nil());
670 JCMethodInvocation eqtest = make.Apply(
671 List.<JCExpression>nil(),
672 make.Select(deserGetter(func, syms.stringType), eqsym).setType(eqmt),
673 List.<JCExpression>of(make.Literal(lit)));
674 eqtest.setType(syms.booleanType);
675 JCBinary compound = make.Binary(JCTree.Tag.AND, prev, eqtest);
676 compound.operator = rs.resolveBinaryOperator(null, JCTree.Tag.AND, attrEnv, syms.booleanType, syms.booleanType);
677 compound.setType(syms.booleanType);
678 return compound;
679 }
681 private JCExpression deserGetter(String func, Type type) {
682 return deserGetter(func, type, List.<Type>nil(), List.<JCExpression>nil());
683 }
685 private JCExpression deserGetter(String func, Type type, List<Type> argTypes, List<JCExpression> args) {
686 MethodType getmt = new MethodType(argTypes, type, List.<Type>nil(), syms.methodClass);
687 Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.<Type>nil());
688 return make.Apply(
689 List.<JCExpression>nil(),
690 make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt),
691 args).setType(type);
692 }
694 /**
695 * Create new synthetic method with given flags, name, type, owner
696 */
697 private MethodSymbol makePrivateSyntheticMethod(long flags, Name name, Type type, Symbol owner) {
698 return new MethodSymbol(flags | SYNTHETIC | PRIVATE, name, type, owner);
699 }
701 /**
702 * Create new synthetic variable with given flags, name, type, owner
703 */
704 private VarSymbol makeSyntheticVar(long flags, String name, Type type, Symbol owner) {
705 return makeSyntheticVar(flags, names.fromString(name), type, owner);
706 }
708 /**
709 * Create new synthetic variable with given flags, name, type, owner
710 */
711 private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) {
712 return new VarSymbol(flags | SYNTHETIC, name, type, owner);
713 }
715 /**
716 * Set varargsElement field on a given tree (must be either a new class tree
717 * or a method call tree)
718 */
719 private void setVarargsIfNeeded(JCTree tree, Type varargsElement) {
720 if (varargsElement != null) {
721 switch (tree.getTag()) {
722 case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break;
723 case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break;
724 default: throw new AssertionError();
725 }
726 }
727 }
729 /**
730 * Convert method/constructor arguments by inserting appropriate cast
731 * as required by type-erasure - this is needed when bridging a lambda/method
732 * reference, as the bridged signature might require downcast to be compatible
733 * with the generated signature.
734 */
735 private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) {
736 Assert.check(meth.kind == Kinds.MTH);
737 List<Type> formals = types.erasure(meth.type).getParameterTypes();
738 if (varargsElement != null) {
739 Assert.check((meth.flags() & VARARGS) != 0);
740 }
741 return transTypes.translateArgs(args, formals, varargsElement, attrEnv);
742 }
744 // </editor-fold>
746 /**
747 * Generate an adapter method "bridge" for a method reference which cannot
748 * be used directly.
749 */
750 private class MemberReferenceBridger {
752 private final JCMemberReference tree;
753 private final ReferenceTranslationContext localContext;
754 private final ListBuffer<JCExpression> args = new ListBuffer<>();
755 private final ListBuffer<JCVariableDecl> params = new ListBuffer<>();
757 MemberReferenceBridger(JCMemberReference tree, ReferenceTranslationContext localContext) {
758 this.tree = tree;
759 this.localContext = localContext;
760 }
762 /**
763 * Generate the bridge
764 */
765 JCMethodDecl bridge() {
766 int prevPos = make.pos;
767 try {
768 make.at(tree);
769 Type samDesc = localContext.bridgedRefSig();
770 List<Type> samPTypes = samDesc.getParameterTypes();
772 //an extra argument is prepended to the signature of the bridge in case
773 //the member reference is an instance method reference (in which case
774 //the receiver expression is passed to the bridge itself).
775 Type recType = null;
776 switch (tree.kind) {
777 case IMPLICIT_INNER:
778 recType = tree.sym.owner.type.getEnclosingType();
779 break;
780 case BOUND:
781 recType = tree.getQualifierExpression().type;
782 break;
783 case UNBOUND:
784 recType = samPTypes.head;
785 samPTypes = samPTypes.tail;
786 break;
787 }
789 //generate the parameter list for the bridged member reference - the
790 //bridge signature will match the signature of the target sam descriptor
792 VarSymbol rcvr = (recType == null)
793 ? null
794 : addParameter("rec$", recType, false);
796 List<Type> refPTypes = tree.sym.type.getParameterTypes();
797 int refSize = refPTypes.size();
798 int samSize = samPTypes.size();
799 // Last parameter to copy from referenced method
800 int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize;
802 List<Type> l = refPTypes;
803 // Use parameter types of the referenced method, excluding final var args
804 for (int i = 0; l.nonEmpty() && i < last; ++i) {
805 addParameter("x$" + i, l.head, true);
806 l = l.tail;
807 }
808 // Flatten out the var args
809 for (int i = last; i < samSize; ++i) {
810 addParameter("xva$" + i, tree.varargsElement, true);
811 }
813 //generate the bridge method declaration
814 JCMethodDecl bridgeDecl = make.MethodDef(make.Modifiers(localContext.bridgeSym.flags()),
815 localContext.bridgeSym.name,
816 make.QualIdent(samDesc.getReturnType().tsym),
817 List.<JCTypeParameter>nil(),
818 params.toList(),
819 tree.sym.type.getThrownTypes() == null
820 ? List.<JCExpression>nil()
821 : make.Types(tree.sym.type.getThrownTypes()),
822 null,
823 null);
824 bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym;
825 bridgeDecl.type = localContext.bridgeSym.type =
826 types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
828 //bridge method body generation - this can be either a method call or a
829 //new instance creation expression, depending on the member reference kind
830 JCExpression bridgeExpr = (tree.getMode() == ReferenceMode.INVOKE)
831 ? bridgeExpressionInvoke(makeReceiver(rcvr))
832 : bridgeExpressionNew();
834 //the body is either a return expression containing a method call,
835 //or the method call itself, depending on whether the return type of
836 //the bridge is non-void/void.
837 bridgeDecl.body = makeLambdaExpressionBody(bridgeExpr, bridgeDecl);
839 return bridgeDecl;
840 } finally {
841 make.at(prevPos);
842 }
843 }
844 //where
845 private JCExpression makeReceiver(VarSymbol rcvr) {
846 if (rcvr == null) return null;
847 JCExpression rcvrExpr = make.Ident(rcvr);
848 Type rcvrType = tree.sym.enclClass().type;
849 if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) {
850 rcvrExpr = make.TypeCast(make.Type(rcvrType), rcvrExpr).setType(rcvrType);
851 }
852 return rcvrExpr;
853 }
855 /**
856 * determine the receiver of the bridged method call - the receiver can
857 * be either the synthetic receiver parameter or a type qualifier; the
858 * original qualifier expression is never used here, as it might refer
859 * to symbols not available in the static context of the bridge
860 */
861 private JCExpression bridgeExpressionInvoke(JCExpression rcvr) {
862 JCExpression qualifier =
863 tree.sym.isStatic() ?
864 make.Type(tree.sym.owner.type) :
865 (rcvr != null) ?
866 rcvr :
867 tree.getQualifierExpression();
869 //create the qualifier expression
870 JCFieldAccess select = make.Select(qualifier, tree.sym.name);
871 select.sym = tree.sym;
872 select.type = tree.sym.erasure(types);
874 //create the method call expression
875 JCExpression apply = make.Apply(List.<JCExpression>nil(), select,
876 convertArgs(tree.sym, args.toList(), tree.varargsElement)).
877 setType(tree.sym.erasure(types).getReturnType());
879 apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType());
880 setVarargsIfNeeded(apply, tree.varargsElement);
881 return apply;
882 }
884 /**
885 * the enclosing expression is either 'null' (no enclosing type) or set
886 * to the first bridge synthetic parameter
887 */
888 private JCExpression bridgeExpressionNew() {
889 if (tree.kind == ReferenceKind.ARRAY_CTOR) {
890 //create the array creation expression
891 JCNewArray newArr = make.NewArray(
892 make.Type(types.elemtype(tree.getQualifierExpression().type)),
893 List.of(make.Ident(params.first())),
894 null);
895 newArr.type = tree.getQualifierExpression().type;
896 return newArr;
897 } else {
898 JCExpression encl = null;
899 switch (tree.kind) {
900 case UNBOUND:
901 case IMPLICIT_INNER:
902 encl = make.Ident(params.first());
903 }
905 //create the instance creation expression
906 JCNewClass newClass = make.NewClass(encl,
907 List.<JCExpression>nil(),
908 make.Type(tree.getQualifierExpression().type),
909 convertArgs(tree.sym, args.toList(), tree.varargsElement),
910 null);
911 newClass.constructor = tree.sym;
912 newClass.constructorType = tree.sym.erasure(types);
913 newClass.type = tree.getQualifierExpression().type;
914 setVarargsIfNeeded(newClass, tree.varargsElement);
915 return newClass;
916 }
917 }
919 private VarSymbol addParameter(String name, Type p, boolean genArg) {
920 VarSymbol vsym = new VarSymbol(0, names.fromString(name), p, localContext.bridgeSym);
921 params.append(make.VarDef(vsym, null));
922 if (genArg) {
923 args.append(make.Ident(vsym));
924 }
925 return vsym;
926 }
927 }
929 /**
930 * Bridges a member reference - this is needed when:
931 * * Var args in the referenced method need to be flattened away
932 * * super is used
933 */
934 private void bridgeMemberReference(JCMemberReference tree, ReferenceTranslationContext localContext) {
935 kInfo.addMethod(new MemberReferenceBridger(tree, localContext).bridge());
936 }
938 private MethodType typeToMethodType(Type mt) {
939 Type type = types.erasure(mt);
940 return new MethodType(type.getParameterTypes(),
941 type.getReturnType(),
942 type.getThrownTypes(),
943 syms.methodClass);
944 }
946 /**
947 * Generate an indy method call to the meta factory
948 */
949 private JCExpression makeMetafactoryIndyCall(TranslationContext<?> context,
950 int refKind, Symbol refSym, List<JCExpression> indy_args) {
951 JCFunctionalExpression tree = context.tree;
952 //determine the static bsm args
953 MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
954 List<Object> staticArgs = List.<Object>of(
955 typeToMethodType(samSym.type),
956 new Pool.MethodHandle(refKind, refSym, types),
957 typeToMethodType(tree.getDescriptorType(types)));
959 //computed indy arg types
960 ListBuffer<Type> indy_args_types = new ListBuffer<>();
961 for (JCExpression arg : indy_args) {
962 indy_args_types.append(arg.type);
963 }
965 //finally, compute the type of the indy call
966 MethodType indyType = new MethodType(indy_args_types.toList(),
967 tree.type,
968 List.<Type>nil(),
969 syms.methodClass);
971 Name metafactoryName = context.needsAltMetafactory() ?
972 names.altMetafactory : names.metafactory;
974 if (context.needsAltMetafactory()) {
975 ListBuffer<Object> markers = new ListBuffer<>();
976 for (Type t : tree.targets.tail) {
977 if (t.tsym != syms.serializableType.tsym) {
978 markers.append(t.tsym);
979 }
980 }
981 int flags = context.isSerializable() ? FLAG_SERIALIZABLE : 0;
982 boolean hasMarkers = markers.nonEmpty();
983 boolean hasBridges = context.bridges.nonEmpty();
984 if (hasMarkers) {
985 flags |= FLAG_MARKERS;
986 }
987 if (hasBridges) {
988 flags |= FLAG_BRIDGES;
989 }
990 staticArgs = staticArgs.append(flags);
991 if (hasMarkers) {
992 staticArgs = staticArgs.append(markers.length());
993 staticArgs = staticArgs.appendList(markers.toList());
994 }
995 if (hasBridges) {
996 staticArgs = staticArgs.append(context.bridges.length() - 1);
997 for (Symbol s : context.bridges) {
998 Type s_erasure = s.erasure(types);
999 if (!types.isSameType(s_erasure, samSym.erasure(types))) {
1000 staticArgs = staticArgs.append(s.erasure(types));
1001 }
1002 }
1003 }
1004 if (context.isSerializable()) {
1005 int prevPos = make.pos;
1006 try {
1007 make.at(kInfo.clazz);
1008 addDeserializationCase(refKind, refSym, tree.type, samSym,
1009 tree, staticArgs, indyType);
1010 } finally {
1011 make.at(prevPos);
1012 }
1013 }
1014 }
1016 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args, samSym.name);
1017 }
1019 /**
1020 * Generate an indy method call with given name, type and static bootstrap
1021 * arguments types
1022 */
1023 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
1024 List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs,
1025 Name methName) {
1026 int prevPos = make.pos;
1027 try {
1028 make.at(pos);
1029 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
1030 syms.stringType,
1031 syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs));
1033 Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site,
1034 bsmName, bsm_staticArgs, List.<Type>nil());
1036 DynamicMethodSymbol dynSym =
1037 new DynamicMethodSymbol(methName,
1038 syms.noSymbol,
1039 bsm.isStatic() ?
1040 ClassFile.REF_invokeStatic :
1041 ClassFile.REF_invokeVirtual,
1042 (MethodSymbol)bsm,
1043 indyType,
1044 staticArgs.toArray());
1046 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName);
1047 qualifier.sym = dynSym;
1048 qualifier.type = indyType.getReturnType();
1050 JCMethodInvocation proxyCall = make.Apply(List.<JCExpression>nil(), qualifier, indyArgs);
1051 proxyCall.type = indyType.getReturnType();
1052 return proxyCall;
1053 } finally {
1054 make.at(prevPos);
1055 }
1056 }
1057 //where
1058 private List<Type> bsmStaticArgToTypes(List<Object> args) {
1059 ListBuffer<Type> argtypes = new ListBuffer<>();
1060 for (Object arg : args) {
1061 argtypes.append(bsmStaticArgToType(arg));
1062 }
1063 return argtypes.toList();
1064 }
1066 private Type bsmStaticArgToType(Object arg) {
1067 Assert.checkNonNull(arg);
1068 if (arg instanceof ClassSymbol) {
1069 return syms.classType;
1070 } else if (arg instanceof Integer) {
1071 return syms.intType;
1072 } else if (arg instanceof Long) {
1073 return syms.longType;
1074 } else if (arg instanceof Float) {
1075 return syms.floatType;
1076 } else if (arg instanceof Double) {
1077 return syms.doubleType;
1078 } else if (arg instanceof String) {
1079 return syms.stringType;
1080 } else if (arg instanceof Pool.MethodHandle) {
1081 return syms.methodHandleType;
1082 } else if (arg instanceof MethodType) {
1083 return syms.methodTypeType;
1084 } else {
1085 Assert.error("bad static arg " + arg.getClass());
1086 return null;
1087 }
1088 }
1090 /**
1091 * Get the opcode associated with this method reference
1092 */
1093 private int referenceKind(Symbol refSym) {
1094 if (refSym.isConstructor()) {
1095 return ClassFile.REF_newInvokeSpecial;
1096 } else {
1097 if (refSym.isStatic()) {
1098 return ClassFile.REF_invokeStatic;
1099 } else if ((refSym.flags() & PRIVATE) != 0) {
1100 return ClassFile.REF_invokeSpecial;
1101 } else if (refSym.enclClass().isInterface()) {
1102 return ClassFile.REF_invokeInterface;
1103 } else {
1104 return ClassFile.REF_invokeVirtual;
1105 }
1106 }
1107 }
1109 // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer">
1110 /**
1111 * This visitor collects information about translation of a lambda expression.
1112 * More specifically, it keeps track of the enclosing contexts and captured locals
1113 * accessed by the lambda being translated (as well as other useful info).
1114 * It also translates away problems for LambdaToMethod.
1115 */
1116 class LambdaAnalyzerPreprocessor extends TreeTranslator {
1118 /** the frame stack - used to reconstruct translation info about enclosing scopes */
1119 private List<Frame> frameStack;
1121 /**
1122 * keep the count of lambda expression (used to generate unambiguous
1123 * names)
1124 */
1125 private int lambdaCount = 0;
1127 /**
1128 * keep the count of lambda expression defined in given context (used to
1129 * generate unambiguous names for serializable lambdas)
1130 */
1131 private class SyntheticMethodNameCounter {
1132 private Map<String, Integer> map = new HashMap<>();
1133 int getIndex(StringBuilder buf) {
1134 String temp = buf.toString();
1135 Integer count = map.get(temp);
1136 if (count == null) {
1137 count = 0;
1138 }
1139 ++count;
1140 map.put(temp, count);
1141 return count;
1142 }
1143 }
1144 private SyntheticMethodNameCounter syntheticMethodNameCounts =
1145 new SyntheticMethodNameCounter();
1147 private Map<Symbol, JCClassDecl> localClassDefs;
1149 /**
1150 * maps for fake clinit symbols to be used as owners of lambda occurring in
1151 * a static var init context
1152 */
1153 private Map<ClassSymbol, Symbol> clinits =
1154 new HashMap<ClassSymbol, Symbol>();
1156 private JCClassDecl analyzeAndPreprocessClass(JCClassDecl tree) {
1157 frameStack = List.nil();
1158 localClassDefs = new HashMap<Symbol, JCClassDecl>();
1159 return translate(tree);
1160 }
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 }
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<ClassSymbol, Symbol>();
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 }
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 }
1255 @Override
1256 public void visitLambda(JCLambda tree) {
1257 List<Frame> prevStack = frameStack;
1258 try {
1259 LambdaTranslationContext context = (LambdaTranslationContext)makeLambdaContext(tree);
1260 frameStack = frameStack.prepend(new Frame(tree));
1261 for (JCVariableDecl param : tree.params) {
1262 context.addSymbol(param.sym, PARAM);
1263 frameStack.head.addLocal(param.sym);
1264 }
1265 contextMap.put(tree, context);
1266 super.visitLambda(tree);
1267 context.complete();
1268 }
1269 finally {
1270 frameStack = prevStack;
1271 }
1272 }
1274 @Override
1275 public void visitMethodDef(JCMethodDecl tree) {
1276 List<Frame> prevStack = frameStack;
1277 try {
1278 frameStack = frameStack.prepend(new Frame(tree));
1279 super.visitMethodDef(tree);
1280 }
1281 finally {
1282 frameStack = prevStack;
1283 }
1284 }
1286 @Override
1287 public void visitNewClass(JCNewClass tree) {
1288 TypeSymbol def = tree.type.tsym;
1289 boolean inReferencedClass = currentlyInClass(def);
1290 boolean isLocal = def.isLocal();
1291 if ((inReferencedClass && isLocal || lambdaNewClassFilter(context(), tree))) {
1292 TranslationContext<?> localContext = context();
1293 while (localContext != null) {
1294 if (localContext.tree.getTag() == LAMBDA) {
1295 ((LambdaTranslationContext)localContext)
1296 .addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS);
1297 }
1298 localContext = localContext.prev;
1299 }
1300 }
1301 if (context() != null && !inReferencedClass && isLocal) {
1302 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context();
1303 captureLocalClassDefs(def, lambdaContext);
1304 }
1305 super.visitNewClass(tree);
1306 }
1307 //where
1308 void captureLocalClassDefs(Symbol csym, final LambdaTranslationContext lambdaContext) {
1309 JCClassDecl localCDef = localClassDefs.get(csym);
1310 if (localCDef != null && lambdaContext.freeVarProcessedLocalClasses.add(csym)) {
1311 BasicFreeVarCollector fvc = lower.new BasicFreeVarCollector() {
1312 @Override
1313 void addFreeVars(ClassSymbol c) {
1314 captureLocalClassDefs(c, lambdaContext);
1315 }
1316 @Override
1317 void visitSymbol(Symbol sym) {
1318 if (sym.kind == VAR &&
1319 sym.owner.kind == MTH &&
1320 ((VarSymbol)sym).getConstValue() == null) {
1321 TranslationContext<?> localContext = context();
1322 while (localContext != null) {
1323 if (localContext.tree.getTag() == LAMBDA) {
1324 JCTree block = capturedDecl(localContext.depth, sym);
1325 if (block == null) break;
1326 ((LambdaTranslationContext)localContext).addSymbol(sym, CAPTURED_VAR);
1327 }
1328 localContext = localContext.prev;
1329 }
1330 }
1331 }
1332 };
1333 fvc.scan(localCDef);
1334 }
1335 }
1336 //where
1337 boolean currentlyInClass(Symbol csym) {
1338 for (Frame frame : frameStack) {
1339 if (frame.tree.hasTag(JCTree.Tag.CLASSDEF)) {
1340 JCClassDecl cdef = (JCClassDecl) frame.tree;
1341 if (cdef.sym == csym) {
1342 return true;
1343 }
1344 }
1345 }
1346 return false;
1347 }
1349 /**
1350 * Method references to local class constructors, may, if the local
1351 * class references local variables, have implicit constructor
1352 * parameters added in Lower; As a result, the invokedynamic bootstrap
1353 * information added in the LambdaToMethod pass will have the wrong
1354 * signature. Hooks between Lower and LambdaToMethod have been added to
1355 * handle normal "new" in this case. This visitor converts potentially
1356 * effected method references into a lambda containing a normal "new" of
1357 * the class.
1358 *
1359 * @param tree
1360 */
1361 @Override
1362 public void visitReference(JCMemberReference tree) {
1363 if (tree.getMode() == ReferenceMode.NEW
1364 && tree.kind != ReferenceKind.ARRAY_CTOR
1365 && tree.sym.owner.isLocal()) {
1366 MethodSymbol consSym = (MethodSymbol) tree.sym;
1367 List<Type> ptypes = ((MethodType) consSym.type).getParameterTypes();
1368 Type classType = consSym.owner.type;
1370 // Build lambda parameters
1371 // partially cloned from TreeMaker.Params until 8014021 is fixed
1372 Symbol owner = owner();
1373 ListBuffer<JCVariableDecl> paramBuff = new ListBuffer<JCVariableDecl>();
1374 int i = 0;
1375 for (List<Type> l = ptypes; l.nonEmpty(); l = l.tail) {
1376 JCVariableDecl param = make.Param(make.paramName(i++), l.head, owner);
1377 param.sym.pos = tree.pos;
1378 paramBuff.append(param);
1379 }
1380 List<JCVariableDecl> params = paramBuff.toList();
1382 // Make new-class call
1383 JCNewClass nc = makeNewClass(classType, make.Idents(params));
1384 nc.pos = tree.pos;
1386 // Make lambda holding the new-class call
1387 JCLambda slam = make.Lambda(params, nc);
1388 slam.targets = tree.targets;
1389 slam.type = tree.type;
1390 slam.pos = tree.pos;
1392 // Now it is a lambda, process as such
1393 visitLambda(slam);
1394 } else {
1395 super.visitReference(tree);
1396 contextMap.put(tree, makeReferenceContext(tree));
1397 }
1398 }
1400 @Override
1401 public void visitSelect(JCFieldAccess tree) {
1402 if (context() != null && tree.sym.kind == VAR &&
1403 (tree.sym.name == names._this ||
1404 tree.sym.name == names._super)) {
1405 // A select of this or super means, if we are in a lambda,
1406 // we much have an instance context
1407 TranslationContext<?> localContext = context();
1408 while (localContext != null) {
1409 if (localContext.tree.hasTag(LAMBDA)) {
1410 JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym);
1411 if (clazz == null) break;
1412 ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS);
1413 }
1414 localContext = localContext.prev;
1415 }
1416 }
1417 super.visitSelect(tree);
1418 }
1420 @Override
1421 public void visitVarDef(JCVariableDecl tree) {
1422 TranslationContext<?> context = context();
1423 LambdaTranslationContext ltc = (context != null && context instanceof LambdaTranslationContext)?
1424 (LambdaTranslationContext)context :
1425 null;
1426 if (ltc != null) {
1427 if (frameStack.head.tree.hasTag(LAMBDA)) {
1428 ltc.addSymbol(tree.sym, LOCAL_VAR);
1429 }
1430 // Check for type variables (including as type arguments).
1431 // If they occur within class nested in a lambda, mark for erasure
1432 Type type = tree.sym.asType();
1433 if (inClassWithinLambda() && !types.isSameType(types.erasure(type), type)) {
1434 ltc.addSymbol(tree.sym, TYPE_VAR);
1435 }
1436 }
1438 List<Frame> prevStack = frameStack;
1439 try {
1440 if (tree.sym.owner.kind == MTH) {
1441 frameStack.head.addLocal(tree.sym);
1442 }
1443 frameStack = frameStack.prepend(new Frame(tree));
1444 super.visitVarDef(tree);
1445 }
1446 finally {
1447 frameStack = prevStack;
1448 }
1449 }
1451 /**
1452 * Return a valid owner given the current declaration stack
1453 * (required to skip synthetic lambda symbols)
1454 */
1455 private Symbol owner() {
1456 return owner(false);
1457 }
1459 @SuppressWarnings("fallthrough")
1460 private Symbol owner(boolean skipLambda) {
1461 List<Frame> frameStack2 = frameStack;
1462 while (frameStack2.nonEmpty()) {
1463 switch (frameStack2.head.tree.getTag()) {
1464 case VARDEF:
1465 if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) {
1466 frameStack2 = frameStack2.tail;
1467 break;
1468 }
1469 JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree;
1470 return initSym(cdecl.sym,
1471 ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC);
1472 case BLOCK:
1473 JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree;
1474 return initSym(cdecl2.sym,
1475 ((JCBlock)frameStack2.head.tree).flags & STATIC);
1476 case CLASSDEF:
1477 return ((JCClassDecl)frameStack2.head.tree).sym;
1478 case METHODDEF:
1479 return ((JCMethodDecl)frameStack2.head.tree).sym;
1480 case LAMBDA:
1481 if (!skipLambda)
1482 return ((LambdaTranslationContext)contextMap
1483 .get(frameStack2.head.tree)).translatedSym;
1484 default:
1485 frameStack2 = frameStack2.tail;
1486 }
1487 }
1488 Assert.error();
1489 return null;
1490 }
1492 private Symbol initSym(ClassSymbol csym, long flags) {
1493 boolean isStatic = (flags & STATIC) != 0;
1494 if (isStatic) {
1495 /* static clinits are generated in Gen, so we need to use a fake
1496 * one. Attr creates a fake clinit method while attributing
1497 * lambda expressions used as initializers of static fields, so
1498 * let's use that one.
1499 */
1500 MethodSymbol clinit = attr.removeClinit(csym);
1501 if (clinit != null) {
1502 clinits.put(csym, clinit);
1503 return clinit;
1504 }
1506 /* if no clinit is found at Attr, then let's try at clinits.
1507 */
1508 clinit = (MethodSymbol)clinits.get(csym);
1509 if (clinit == null) {
1510 /* no luck, let's create a new one
1511 */
1512 clinit = makePrivateSyntheticMethod(STATIC,
1513 names.clinit,
1514 new MethodType(List.<Type>nil(), syms.voidType,
1515 List.<Type>nil(), syms.methodClass),
1516 csym);
1517 clinits.put(csym, clinit);
1518 }
1519 return clinit;
1520 } else {
1521 //get the first constructor and treat it as the instance init sym
1522 for (Symbol s : csym.members_field.getElementsByName(names.init)) {
1523 return s;
1524 }
1525 }
1526 Assert.error("init not found");
1527 return null;
1528 }
1530 private JCTree directlyEnclosingLambda() {
1531 if (frameStack.isEmpty()) {
1532 return null;
1533 }
1534 List<Frame> frameStack2 = frameStack;
1535 while (frameStack2.nonEmpty()) {
1536 switch (frameStack2.head.tree.getTag()) {
1537 case CLASSDEF:
1538 case METHODDEF:
1539 return null;
1540 case LAMBDA:
1541 return frameStack2.head.tree;
1542 default:
1543 frameStack2 = frameStack2.tail;
1544 }
1545 }
1546 Assert.error();
1547 return null;
1548 }
1550 private boolean inClassWithinLambda() {
1551 if (frameStack.isEmpty()) {
1552 return false;
1553 }
1554 List<Frame> frameStack2 = frameStack;
1555 boolean classFound = false;
1556 while (frameStack2.nonEmpty()) {
1557 switch (frameStack2.head.tree.getTag()) {
1558 case LAMBDA:
1559 return classFound;
1560 case CLASSDEF:
1561 classFound = true;
1562 frameStack2 = frameStack2.tail;
1563 break;
1564 default:
1565 frameStack2 = frameStack2.tail;
1566 }
1567 }
1568 // No lambda
1569 return false;
1570 }
1572 /**
1573 * Return the declaration corresponding to a symbol in the enclosing
1574 * scope; the depth parameter is used to filter out symbols defined
1575 * in nested scopes (which do not need to undergo capture).
1576 */
1577 private JCTree capturedDecl(int depth, Symbol sym) {
1578 int currentDepth = frameStack.size() - 1;
1579 for (Frame block : frameStack) {
1580 switch (block.tree.getTag()) {
1581 case CLASSDEF:
1582 ClassSymbol clazz = ((JCClassDecl)block.tree).sym;
1583 if (sym.isMemberOf(clazz, types)) {
1584 return currentDepth > depth ? null : block.tree;
1585 }
1586 break;
1587 case VARDEF:
1588 if (((JCVariableDecl)block.tree).sym == sym &&
1589 sym.owner.kind == MTH) { //only locals are captured
1590 return currentDepth > depth ? null : block.tree;
1591 }
1592 break;
1593 case BLOCK:
1594 case METHODDEF:
1595 case LAMBDA:
1596 if (block.locals != null && block.locals.contains(sym)) {
1597 return currentDepth > depth ? null : block.tree;
1598 }
1599 break;
1600 default:
1601 Assert.error("bad decl kind " + block.tree.getTag());
1602 }
1603 currentDepth--;
1604 }
1605 return null;
1606 }
1608 private TranslationContext<?> context() {
1609 for (Frame frame : frameStack) {
1610 TranslationContext<?> context = contextMap.get(frame.tree);
1611 if (context != null) {
1612 return context;
1613 }
1614 }
1615 return null;
1616 }
1618 /**
1619 * This is used to filter out those identifiers that needs to be adjusted
1620 * when translating away lambda expressions
1621 */
1622 private boolean lambdaIdentSymbolFilter(Symbol sym) {
1623 return (sym.kind == VAR || sym.kind == MTH)
1624 && !sym.isStatic()
1625 && sym.name != names.init;
1626 }
1628 /**
1629 * This is used to filter out those new class expressions that need to
1630 * be qualified with an enclosing tree
1631 */
1632 private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) {
1633 if (context != null
1634 && tree.encl == null
1635 && tree.def == null
1636 && !tree.type.getEnclosingType().hasTag(NONE)) {
1637 Type encl = tree.type.getEnclosingType();
1638 Type current = context.owner.enclClass().type;
1639 while (!current.hasTag(NONE)) {
1640 if (current.tsym.isSubClass(encl.tsym, types)) {
1641 return true;
1642 }
1643 current = current.getEnclosingType();
1644 }
1645 return false;
1646 } else {
1647 return false;
1648 }
1649 }
1651 private TranslationContext<JCLambda> makeLambdaContext(JCLambda tree) {
1652 return new LambdaTranslationContext(tree);
1653 }
1655 private TranslationContext<JCMemberReference> makeReferenceContext(JCMemberReference tree) {
1656 return new ReferenceTranslationContext(tree);
1657 }
1659 private class Frame {
1660 final JCTree tree;
1661 List<Symbol> locals;
1663 public Frame(JCTree tree) {
1664 this.tree = tree;
1665 }
1667 void addLocal(Symbol sym) {
1668 if (locals == null) {
1669 locals = List.nil();
1670 }
1671 locals = locals.prepend(sym);
1672 }
1673 }
1675 /**
1676 * This class is used to store important information regarding translation of
1677 * lambda expression/method references (see subclasses).
1678 */
1679 private abstract class TranslationContext<T extends JCFunctionalExpression> {
1681 /** the underlying (untranslated) tree */
1682 final T tree;
1684 /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */
1685 final Symbol owner;
1687 /** the depth of this lambda expression in the frame stack */
1688 final int depth;
1690 /** the enclosing translation context (set for nested lambdas/mref) */
1691 final TranslationContext<?> prev;
1693 /** list of methods to be bridged by the meta-factory */
1694 final List<Symbol> bridges;
1696 TranslationContext(T tree) {
1697 this.tree = tree;
1698 this.owner = owner();
1699 this.depth = frameStack.size() - 1;
1700 this.prev = context();
1701 ClassSymbol csym =
1702 types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.targets, ABSTRACT | INTERFACE);
1703 this.bridges = types.functionalInterfaceBridges(csym);
1704 }
1706 /** does this functional expression need to be created using alternate metafactory? */
1707 boolean needsAltMetafactory() {
1708 return tree.targets.length() > 1 ||
1709 isSerializable() ||
1710 bridges.length() > 1;
1711 }
1713 /** does this functional expression require serialization support? */
1714 boolean isSerializable() {
1715 if (forceSerializable) {
1716 return true;
1717 }
1718 for (Type target : tree.targets) {
1719 if (types.asSuper(target, syms.serializableType.tsym) != null) {
1720 return true;
1721 }
1722 }
1723 return false;
1724 }
1726 /**
1727 * @return Name of the enclosing method to be folded into synthetic
1728 * method name
1729 */
1730 String enclosingMethodName() {
1731 return syntheticMethodNameComponent(owner.name);
1732 }
1734 /**
1735 * @return Method name in a form that can be folded into a
1736 * component of a synthetic method name
1737 */
1738 String syntheticMethodNameComponent(Name name) {
1739 if (name == null) {
1740 return "null";
1741 }
1742 String methodName = name.toString();
1743 if (methodName.equals("<clinit>")) {
1744 methodName = "static";
1745 } else if (methodName.equals("<init>")) {
1746 methodName = "new";
1747 }
1748 return methodName;
1749 }
1750 }
1752 /**
1753 * This class retains all the useful information about a lambda expression;
1754 * the contents of this class are filled by the LambdaAnalyzer visitor,
1755 * and the used by the main translation routines in order to adjust references
1756 * to captured locals/members, etc.
1757 */
1758 private class LambdaTranslationContext extends TranslationContext<JCLambda> {
1760 /** variable in the enclosing context to which this lambda is assigned */
1761 final Symbol self;
1763 /** variable in the enclosing context to which this lambda is assigned */
1764 final Symbol assignedTo;
1766 Map<LambdaSymbolKind, Map<Symbol, Symbol>> translatedSymbols;
1768 /** the synthetic symbol for the method hoisting the translated lambda */
1769 Symbol translatedSym;
1771 List<JCVariableDecl> syntheticParams;
1773 /**
1774 * to prevent recursion, track local classes processed
1775 */
1776 final Set<Symbol> freeVarProcessedLocalClasses;
1778 LambdaTranslationContext(JCLambda tree) {
1779 super(tree);
1780 Frame frame = frameStack.head;
1781 switch (frame.tree.getTag()) {
1782 case VARDEF:
1783 assignedTo = self = ((JCVariableDecl) frame.tree).sym;
1784 break;
1785 case ASSIGN:
1786 self = null;
1787 assignedTo = TreeInfo.symbol(((JCAssign) frame.tree).getVariable());
1788 break;
1789 default:
1790 assignedTo = self = null;
1791 break;
1792 }
1794 // This symbol will be filled-in in complete
1795 this.translatedSym = makePrivateSyntheticMethod(0, null, null, owner.enclClass());
1797 if (dumpLambdaToMethodStats) {
1798 log.note(tree, "lambda.stat", needsAltMetafactory(), translatedSym);
1799 }
1800 translatedSymbols = new EnumMap<>(LambdaSymbolKind.class);
1802 translatedSymbols.put(PARAM, new LinkedHashMap<Symbol, Symbol>());
1803 translatedSymbols.put(LOCAL_VAR, new LinkedHashMap<Symbol, Symbol>());
1804 translatedSymbols.put(CAPTURED_VAR, new LinkedHashMap<Symbol, Symbol>());
1805 translatedSymbols.put(CAPTURED_THIS, new LinkedHashMap<Symbol, Symbol>());
1806 translatedSymbols.put(TYPE_VAR, new LinkedHashMap<Symbol, Symbol>());
1808 freeVarProcessedLocalClasses = new HashSet<>();
1809 }
1811 /**
1812 * For a serializable lambda, generate a disambiguating string
1813 * which maximizes stability across deserialization.
1814 *
1815 * @return String to differentiate synthetic lambda method names
1816 */
1817 private String serializedLambdaDisambiguation() {
1818 StringBuilder buf = new StringBuilder();
1819 // Append the enclosing method signature to differentiate
1820 // overloaded enclosing methods. For lambdas enclosed in
1821 // lambdas, the generated lambda method will not have type yet,
1822 // but the enclosing method's name will have been generated
1823 // with this same method, so it will be unique and never be
1824 // overloaded.
1825 Assert.check(
1826 owner.type != null ||
1827 directlyEnclosingLambda() != null);
1828 if (owner.type != null) {
1829 buf.append(typeSig(owner.type));
1830 buf.append(":");
1831 }
1833 // Add target type info
1834 buf.append(types.findDescriptorSymbol(tree.type.tsym).owner.flatName());
1835 buf.append(" ");
1837 // Add variable assigned to
1838 if (assignedTo != null) {
1839 buf.append(assignedTo.flatName());
1840 buf.append("=");
1841 }
1842 //add captured locals info: type, name, order
1843 for (Symbol fv : getSymbolMap(CAPTURED_VAR).keySet()) {
1844 if (fv != self) {
1845 buf.append(typeSig(fv.type));
1846 buf.append(" ");
1847 buf.append(fv.flatName());
1848 buf.append(",");
1849 }
1850 }
1852 return buf.toString();
1853 }
1855 /**
1856 * For a non-serializable lambda, generate a simple method.
1857 *
1858 * @return Name to use for the synthetic lambda method name
1859 */
1860 private Name lambdaName() {
1861 return names.lambda.append(names.fromString(enclosingMethodName() + "$" + lambdaCount++));
1862 }
1864 /**
1865 * For a serializable lambda, generate a method name which maximizes
1866 * name stability across deserialization.
1867 *
1868 * @return Name to use for the synthetic lambda method name
1869 */
1870 private Name serializedLambdaName() {
1871 StringBuilder buf = new StringBuilder();
1872 buf.append(names.lambda);
1873 // Append the name of the method enclosing the lambda.
1874 buf.append(enclosingMethodName());
1875 buf.append('$');
1876 // Append a hash of the disambiguating string : enclosing method
1877 // signature, etc.
1878 String disam = serializedLambdaDisambiguation();
1879 buf.append(Integer.toHexString(disam.hashCode()));
1880 buf.append('$');
1881 // The above appended name components may not be unique, append
1882 // a count based on the above name components.
1883 buf.append(syntheticMethodNameCounts.getIndex(buf));
1884 String result = buf.toString();
1885 //System.err.printf("serializedLambdaName: %s -- %s\n", result, disam);
1886 return names.fromString(result);
1887 }
1889 /**
1890 * Translate a symbol of a given kind into something suitable for the
1891 * synthetic lambda body
1892 */
1893 Symbol translate(Name name, final Symbol sym, LambdaSymbolKind skind) {
1894 Symbol ret;
1895 switch (skind) {
1896 case CAPTURED_THIS:
1897 ret = sym; // self represented
1898 break;
1899 case TYPE_VAR:
1900 // Just erase the type var
1901 ret = new VarSymbol(sym.flags(), name,
1902 types.erasure(sym.type), sym.owner);
1904 /* this information should also be kept for LVT generation at Gen
1905 * a Symbol with pos < startPos won't be tracked.
1906 */
1907 ((VarSymbol)ret).pos = ((VarSymbol)sym).pos;
1908 break;
1909 case CAPTURED_VAR:
1910 ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym) {
1911 @Override
1912 public Symbol baseSymbol() {
1913 //keep mapping with original captured symbol
1914 return sym;
1915 }
1916 };
1917 break;
1918 case LOCAL_VAR:
1919 ret = new VarSymbol(sym.flags() & FINAL, name, sym.type, translatedSym);
1920 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
1921 break;
1922 case PARAM:
1923 ret = new VarSymbol((sym.flags() & FINAL) | PARAMETER, name, types.erasure(sym.type), translatedSym);
1924 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
1925 break;
1926 default:
1927 ret = makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
1928 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
1929 }
1930 if (ret != sym) {
1931 ret.setDeclarationAttributes(sym.getRawAttributes());
1932 ret.setTypeAttributes(sym.getRawTypeAttributes());
1933 }
1934 return ret;
1935 }
1937 void addSymbol(Symbol sym, LambdaSymbolKind skind) {
1938 Map<Symbol, Symbol> transMap = getSymbolMap(skind);
1939 Name preferredName;
1940 switch (skind) {
1941 case CAPTURED_THIS:
1942 preferredName = names.fromString("encl$" + transMap.size());
1943 break;
1944 case CAPTURED_VAR:
1945 preferredName = names.fromString("cap$" + transMap.size());
1946 break;
1947 case LOCAL_VAR:
1948 preferredName = sym.name;
1949 break;
1950 case PARAM:
1951 preferredName = sym.name;
1952 break;
1953 case TYPE_VAR:
1954 preferredName = sym.name;
1955 break;
1956 default: throw new AssertionError();
1957 }
1958 if (!transMap.containsKey(sym)) {
1959 transMap.put(sym, translate(preferredName, sym, skind));
1960 }
1961 }
1963 Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind skind) {
1964 Map<Symbol, Symbol> m = translatedSymbols.get(skind);
1965 Assert.checkNonNull(m);
1966 return m;
1967 }
1969 JCTree translate(JCIdent lambdaIdent) {
1970 for (Map<Symbol, Symbol> m : translatedSymbols.values()) {
1971 if (m.containsKey(lambdaIdent.sym)) {
1972 Symbol tSym = m.get(lambdaIdent.sym);
1973 JCTree t = make.Ident(tSym).setType(lambdaIdent.type);
1974 tSym.setTypeAttributes(lambdaIdent.sym.getRawTypeAttributes());
1975 return t;
1976 }
1977 }
1978 return null;
1979 }
1981 /**
1982 * The translatedSym is not complete/accurate until the analysis is
1983 * finished. Once the analysis is finished, the translatedSym is
1984 * "completed" -- updated with type information, access modifiers,
1985 * and full parameter list.
1986 */
1987 void complete() {
1988 if (syntheticParams != null) {
1989 return;
1990 }
1991 boolean inInterface = translatedSym.owner.isInterface();
1992 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty();
1994 // If instance access isn't needed, make it static.
1995 // Interface instance methods must be default methods.
1996 // Lambda methods are private synthetic.
1997 translatedSym.flags_field = SYNTHETIC | LAMBDA_METHOD |
1998 PRIVATE |
1999 (thisReferenced? (inInterface? DEFAULT : 0) : STATIC);
2001 //compute synthetic params
2002 ListBuffer<JCVariableDecl> params = new ListBuffer<>();
2004 // The signature of the method is augmented with the following
2005 // synthetic parameters:
2006 //
2007 // 1) reference to enclosing contexts captured by the lambda expression
2008 // 2) enclosing locals captured by the lambda expression
2009 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR).values()) {
2010 params.append(make.VarDef((VarSymbol) thisSym, null));
2011 }
2012 for (Symbol thisSym : getSymbolMap(PARAM).values()) {
2013 params.append(make.VarDef((VarSymbol) thisSym, null));
2014 }
2015 syntheticParams = params.toList();
2017 // Compute and set the lambda name
2018 translatedSym.name = isSerializable()
2019 ? serializedLambdaName()
2020 : lambdaName();
2022 //prepend synthetic args to translated lambda method signature
2023 translatedSym.type = types.createMethodTypeWithParameters(
2024 generatedLambdaSig(),
2025 TreeInfo.types(syntheticParams));
2026 }
2028 Type generatedLambdaSig() {
2029 return types.erasure(tree.getDescriptorType(types));
2030 }
2031 }
2033 /**
2034 * This class retains all the useful information about a method reference;
2035 * the contents of this class are filled by the LambdaAnalyzer visitor,
2036 * and the used by the main translation routines in order to adjust method
2037 * references (i.e. in case a bridge is needed)
2038 */
2039 private class ReferenceTranslationContext extends TranslationContext<JCMemberReference> {
2041 final boolean isSuper;
2042 final Symbol bridgeSym;
2043 final Symbol sigPolySym;
2045 ReferenceTranslationContext(JCMemberReference tree) {
2046 super(tree);
2047 this.isSuper = tree.hasKind(ReferenceKind.SUPER);
2048 this.bridgeSym = needsBridge()
2049 ? makePrivateSyntheticMethod(isSuper ? 0 : STATIC,
2050 referenceBridgeName(), null,
2051 owner.enclClass())
2052 : null;
2053 this.sigPolySym = isSignaturePolymorphic()
2054 ? makePrivateSyntheticMethod(tree.sym.flags(),
2055 tree.sym.name,
2056 bridgedRefSig(),
2057 tree.sym.enclClass())
2058 : null;
2059 if (dumpLambdaToMethodStats) {
2060 String key = bridgeSym == null ?
2061 "mref.stat" : "mref.stat.1";
2062 log.note(tree, key, needsAltMetafactory(), bridgeSym);
2063 }
2064 }
2066 /**
2067 * Get the opcode associated with this method reference
2068 */
2069 int referenceKind() {
2070 return LambdaToMethod.this.referenceKind(needsBridge()
2071 ? bridgeSym
2072 : tree.sym);
2073 }
2075 boolean needsVarArgsConversion() {
2076 return tree.varargsElement != null;
2077 }
2079 /**
2080 * Generate a disambiguating string to increase stability (important
2081 * if serialized)
2082 *
2083 * @return String to differentiate synthetic lambda method names
2084 */
2085 private String referenceBridgeDisambiguation() {
2086 StringBuilder buf = new StringBuilder();
2087 // Append the enclosing method signature to differentiate
2088 // overloaded enclosing methods.
2089 if (owner.type != null) {
2090 buf.append(typeSig(owner.type));
2091 buf.append(":");
2092 }
2094 // Append qualifier type
2095 buf.append(classSig(tree.sym.owner.type));
2097 // Note static/instance
2098 buf.append(tree.sym.isStatic()? " S " : " I ");
2100 // Append referenced signature
2101 buf.append(typeSig(tree.sym.erasure(types)));
2103 return buf.toString();
2104 }
2106 /**
2107 * Construct a unique stable name for the method reference bridge
2108 *
2109 * @return Name to use for the synthetic method name
2110 */
2111 private Name referenceBridgeName() {
2112 StringBuilder buf = new StringBuilder();
2113 // Append lambda ID, this is semantically significant
2114 buf.append(names.lambda);
2115 // Note that it is a method reference bridge
2116 buf.append("MR$");
2117 // Append the enclosing method name
2118 buf.append(enclosingMethodName());
2119 buf.append('$');
2120 // Append the referenced method name
2121 buf.append(syntheticMethodNameComponent(tree.sym.name));
2122 buf.append('$');
2123 // Append a hash of the disambiguating string : enclosing method
2124 // signature, etc.
2125 String disam = referenceBridgeDisambiguation();
2126 buf.append(Integer.toHexString(disam.hashCode()));
2127 buf.append('$');
2128 // The above appended name components may not be unique, append
2129 // a count based on the above name components.
2130 buf.append(syntheticMethodNameCounts.getIndex(buf));
2131 String result = buf.toString();
2132 return names.fromString(result);
2133 }
2135 /**
2136 * @return Is this an array operation like clone()
2137 */
2138 boolean isArrayOp() {
2139 return tree.sym.owner == syms.arrayClass;
2140 }
2142 boolean receiverAccessible() {
2143 //hack needed to workaround 292 bug (7087658)
2144 //when 292 issue is fixed we should remove this and change the backend
2145 //code to always generate a method handle to an accessible method
2146 return tree.ownerAccessible;
2147 }
2149 /**
2150 * The VM does not support access across nested classes (8010319).
2151 * Were that ever to change, this should be removed.
2152 */
2153 boolean isPrivateInOtherClass() {
2154 return (tree.sym.flags() & PRIVATE) != 0 &&
2155 !types.isSameType(
2156 types.erasure(tree.sym.enclClass().asType()),
2157 types.erasure(owner.enclClass().asType()));
2158 }
2160 /**
2161 * Signature polymorphic methods need special handling.
2162 * e.g. MethodHandle.invoke() MethodHandle.invokeExact()
2163 */
2164 final boolean isSignaturePolymorphic() {
2165 return tree.sym.kind == MTH &&
2166 types.isSignaturePolymorphic((MethodSymbol)tree.sym);
2167 }
2169 /**
2170 * Does this reference needs a bridge (i.e. var args need to be
2171 * expanded or "super" is used)
2172 */
2173 final boolean needsBridge() {
2174 return isSuper || needsVarArgsConversion() || isArrayOp() ||
2175 isPrivateInOtherClass() ||
2176 !receiverAccessible();
2177 }
2179 Type generatedRefSig() {
2180 return types.erasure(tree.sym.type);
2181 }
2183 Type bridgedRefSig() {
2184 return types.erasure(types.findDescriptorSymbol(tree.targets.head.tsym).type);
2185 }
2186 }
2187 }
2188 // </editor-fold>
2190 /*
2191 * These keys provide mappings for various translated lambda symbols
2192 * and the prevailing order must be maintained.
2193 */
2194 enum LambdaSymbolKind {
2195 PARAM, // original to translated lambda parameters
2196 LOCAL_VAR, // original to translated lambda locals
2197 CAPTURED_VAR, // variables in enclosing scope to translated synthetic parameters
2198 CAPTURED_THIS, // class symbols to translated synthetic parameters (for captured member access)
2199 TYPE_VAR; // original to translated lambda type variables
2200 }
2202 /**
2203 * ****************************************************************
2204 * Signature Generation
2205 * ****************************************************************
2206 */
2208 private String typeSig(Type type) {
2209 L2MSignatureGenerator sg = new L2MSignatureGenerator();
2210 sg.assembleSig(type);
2211 return sg.toString();
2212 }
2214 private String classSig(Type type) {
2215 L2MSignatureGenerator sg = new L2MSignatureGenerator();
2216 sg.assembleClassSig(type);
2217 return sg.toString();
2218 }
2220 /**
2221 * Signature Generation
2222 */
2223 private class L2MSignatureGenerator extends Types.SignatureGenerator {
2225 /**
2226 * An output buffer for type signatures.
2227 */
2228 StringBuilder sb = new StringBuilder();
2230 L2MSignatureGenerator() {
2231 super(types);
2232 }
2234 @Override
2235 protected void append(char ch) {
2236 sb.append(ch);
2237 }
2239 @Override
2240 protected void append(byte[] ba) {
2241 sb.append(new String(ba));
2242 }
2244 @Override
2245 protected void append(Name name) {
2246 sb.append(name.toString());
2247 }
2249 @Override
2250 public String toString() {
2251 return sb.toString();
2252 }
2253 }
2254 }