Fri, 26 Apr 2013 10:17:01 +0100
8008562: javac, a refactoring to Bits is necessary in order to provide a change history
Reviewed-by: mcimadamore
1 /*
2 * Copyright (c) 2010, 2013, 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.*;
30 import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind;
31 import com.sun.tools.javac.tree.TreeMaker;
32 import com.sun.tools.javac.tree.TreeScanner;
33 import com.sun.tools.javac.tree.TreeTranslator;
34 import com.sun.tools.javac.code.Kinds;
35 import com.sun.tools.javac.code.Scope;
36 import com.sun.tools.javac.code.Symbol;
37 import com.sun.tools.javac.code.Symbol.ClassSymbol;
38 import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol;
39 import com.sun.tools.javac.code.Symbol.MethodSymbol;
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.ClassType;
44 import com.sun.tools.javac.code.Type.MethodType;
45 import com.sun.tools.javac.code.Types;
46 import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzer.*;
47 import com.sun.tools.javac.comp.Lower.BasicFreeVarCollector;
48 import com.sun.tools.javac.jvm.*;
49 import com.sun.tools.javac.util.*;
50 import com.sun.tools.javac.util.List;
51 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
52 import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
54 import java.util.HashMap;
55 import java.util.LinkedHashMap;
56 import java.util.Map;
58 import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*;
59 import static com.sun.tools.javac.code.Flags.*;
60 import static com.sun.tools.javac.code.Kinds.*;
61 import static com.sun.tools.javac.code.TypeTag.*;
62 import static com.sun.tools.javac.tree.JCTree.Tag.*;
64 /**
65 * This pass desugars lambda expressions into static methods
66 *
67 * <p><b>This is NOT part of any supported API.
68 * If you write code that depends on this, you do so at your own risk.
69 * This code and its internal interfaces are subject to change or
70 * deletion without notice.</b>
71 */
72 public class LambdaToMethod extends TreeTranslator {
74 private Lower lower;
75 private Names names;
76 private Symtab syms;
77 private Resolve rs;
78 private TreeMaker make;
79 private Types types;
80 private TransTypes transTypes;
81 private Env<AttrContext> attrEnv;
83 /** the analyzer scanner */
84 private LambdaAnalyzer analyzer;
86 /** map from lambda trees to translation contexts */
87 private Map<JCTree, TranslationContext<?>> contextMap;
89 /** current translation context (visitor argument) */
90 private TranslationContext<?> context;
92 /** info about the current class being processed */
93 private KlassInfo kInfo;
95 /** Flag for alternate metafactories indicating the lambda object is intended to be serializable */
96 public static final int FLAG_SERIALIZABLE = 1 << 0;
98 /** Flag for alternate metafactories indicating the lambda object has multiple targets */
99 public static final int FLAG_MARKERS = 1 << 1;
101 private class KlassInfo {
103 /**
104 * list of methods to append
105 */
106 private ListBuffer<JCTree> appendedMethodList;
108 /**
109 * list of deserialization cases
110 */
111 private final Map<String, ListBuffer<JCStatement>> deserializeCases;
113 /**
114 * deserialize method symbol
115 */
116 private final MethodSymbol deserMethodSym;
118 /**
119 * deserialize method parameter symbol
120 */
121 private final VarSymbol deserParamSym;
123 private KlassInfo(Symbol kSym) {
124 appendedMethodList = ListBuffer.lb();
125 deserializeCases = new HashMap<String, ListBuffer<JCStatement>>();
126 long flags = PRIVATE | STATIC | SYNTHETIC;
127 MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType,
128 List.<Type>nil(), syms.methodClass);
129 deserMethodSym = makeSyntheticMethod(flags, names.deserializeLambda, type, kSym);
130 deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"),
131 syms.serializedLambdaType, deserMethodSym);
132 }
134 private void addMethod(JCTree decl) {
135 appendedMethodList = appendedMethodList.prepend(decl);
136 }
137 }
139 // <editor-fold defaultstate="collapsed" desc="Instantiating">
140 private static final Context.Key<LambdaToMethod> unlambdaKey =
141 new Context.Key<LambdaToMethod>();
143 public static LambdaToMethod instance(Context context) {
144 LambdaToMethod instance = context.get(unlambdaKey);
145 if (instance == null) {
146 instance = new LambdaToMethod(context);
147 }
148 return instance;
149 }
151 private LambdaToMethod(Context context) {
152 lower = Lower.instance(context);
153 names = Names.instance(context);
154 syms = Symtab.instance(context);
155 rs = Resolve.instance(context);
156 make = TreeMaker.instance(context);
157 types = Types.instance(context);
158 transTypes = TransTypes.instance(context);
159 analyzer = new LambdaAnalyzer();
160 }
161 // </editor-fold>
163 // <editor-fold defaultstate="collapsed" desc="translate methods">
164 @Override
165 public <T extends JCTree> T translate(T tree) {
166 TranslationContext<?> newContext = contextMap.get(tree);
167 return translate(tree, newContext != null ? newContext : context);
168 }
170 public <T extends JCTree> T translate(T tree, TranslationContext<?> newContext) {
171 TranslationContext<?> prevContext = context;
172 try {
173 context = newContext;
174 return super.translate(tree);
175 }
176 finally {
177 context = prevContext;
178 }
179 }
181 public <T extends JCTree> List<T> translate(List<T> trees, TranslationContext<?> newContext) {
182 ListBuffer<T> buf = ListBuffer.lb();
183 for (T tree : trees) {
184 buf.append(translate(tree, newContext));
185 }
186 return buf.toList();
187 }
189 public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
190 this.make = make;
191 this.attrEnv = env;
192 this.context = null;
193 this.contextMap = new HashMap<JCTree, TranslationContext<?>>();
194 return translate(cdef);
195 }
196 // </editor-fold>
198 // <editor-fold defaultstate="collapsed" desc="visitor methods">
199 /**
200 * Visit a class.
201 * Maintain the translatedMethodList across nested classes.
202 * Append the translatedMethodList to the class after it is translated.
203 * @param tree
204 */
205 @Override
206 public void visitClassDef(JCClassDecl tree) {
207 if (tree.sym.owner.kind == PCK) {
208 //analyze class
209 analyzer.analyzeClass(tree);
210 }
211 KlassInfo prevKlassInfo = kInfo;
212 try {
213 kInfo = new KlassInfo(tree.sym);
214 super.visitClassDef(tree);
215 if (!kInfo.deserializeCases.isEmpty()) {
216 kInfo.addMethod(makeDeserializeMethod(tree.sym));
217 }
218 //add all translated instance methods here
219 List<JCTree> newMethods = kInfo.appendedMethodList.toList();
220 tree.defs = tree.defs.appendList(newMethods);
221 for (JCTree lambda : newMethods) {
222 tree.sym.members().enter(((JCMethodDecl)lambda).sym);
223 }
224 result = tree;
225 } finally {
226 kInfo = prevKlassInfo;
227 }
228 }
230 /**
231 * Translate a lambda into a method to be inserted into the class.
232 * Then replace the lambda site with an invokedynamic call of to lambda
233 * meta-factory, which will use the lambda method.
234 * @param tree
235 */
236 @Override
237 public void visitLambda(JCLambda tree) {
238 LambdaTranslationContext localContext = (LambdaTranslationContext)context;
239 MethodSymbol sym = (MethodSymbol)localContext.translatedSym;
240 MethodType lambdaType = (MethodType) sym.type;
242 //create the method declaration hoisting the lambda body
243 JCMethodDecl lambdaDecl = make.MethodDef(make.Modifiers(sym.flags_field),
244 sym.name,
245 make.QualIdent(lambdaType.getReturnType().tsym),
246 List.<JCTypeParameter>nil(),
247 localContext.syntheticParams,
248 lambdaType.getThrownTypes() == null ?
249 List.<JCExpression>nil() :
250 make.Types(lambdaType.getThrownTypes()),
251 null,
252 null);
253 lambdaDecl.sym = sym;
254 lambdaDecl.type = lambdaType;
256 //translate lambda body
257 //As the lambda body is translated, all references to lambda locals,
258 //captured variables, enclosing members are adjusted accordingly
259 //to refer to the static method parameters (rather than i.e. acessing to
260 //captured members directly).
261 lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl));
263 //Add the method to the list of methods to be added to this class.
264 kInfo.addMethod(lambdaDecl);
266 //now that we have generated a method for the lambda expression,
267 //we can translate the lambda into a method reference pointing to the newly
268 //created method.
269 //
270 //Note that we need to adjust the method handle so that it will match the
271 //signature of the SAM descriptor - this means that the method reference
272 //should be added the following synthetic arguments:
273 //
274 // * the "this" argument if it is an instance method
275 // * enclosing locals captured by the lambda expression
277 ListBuffer<JCExpression> syntheticInits = ListBuffer.lb();
279 if (!sym.isStatic()) {
280 syntheticInits.append(makeThis(
281 sym.owner.enclClass().asType(),
282 localContext.owner.enclClass()));
283 }
285 //add captured locals
286 for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) {
287 if (fv != localContext.self) {
288 JCTree captured_local = make.Ident(fv).setType(fv.type);
289 syntheticInits.append((JCExpression) captured_local);
290 }
291 }
293 //then, determine the arguments to the indy call
294 List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev);
296 //build a sam instance using an indy call to the meta-factory
297 int refKind = referenceKind(sym);
299 //convert to an invokedynamic call
300 result = makeMetaFactoryIndyCall(tree, context.needsAltMetafactory(), context.isSerializable(), refKind, sym, indy_args);
301 }
303 private JCIdent makeThis(Type type, Symbol owner) {
304 VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC,
305 names._this,
306 type,
307 owner);
308 return make.Ident(_this);
309 }
311 /**
312 * Translate a method reference into an invokedynamic call to the
313 * meta-factory.
314 * @param tree
315 */
316 @Override
317 public void visitReference(JCMemberReference tree) {
318 ReferenceTranslationContext localContext = (ReferenceTranslationContext)context;
320 //first determine the method symbol to be used to generate the sam instance
321 //this is either the method reference symbol, or the bridged reference symbol
322 Symbol refSym = localContext.needsBridge() ?
323 localContext.bridgeSym :
324 tree.sym;
326 //build the bridge method, if needed
327 if (localContext.needsBridge()) {
328 bridgeMemberReference(tree, localContext);
329 }
331 //the qualifying expression is treated as a special captured arg
332 JCExpression init;
333 switch(tree.kind) {
335 case IMPLICIT_INNER: /** Inner :: new */
336 case SUPER: /** super :: instMethod */
337 init = makeThis(
338 localContext.owner.enclClass().asType(),
339 localContext.owner.enclClass());
340 break;
342 case BOUND: /** Expr :: instMethod */
343 init = tree.getQualifierExpression();
344 break;
346 case UNBOUND: /** Type :: instMethod */
347 case STATIC: /** Type :: staticMethod */
348 case TOPLEVEL: /** Top level :: new */
349 case ARRAY_CTOR: /** ArrayType :: new */
350 init = null;
351 break;
353 default:
354 throw new InternalError("Should not have an invalid kind");
355 }
357 List<JCExpression> indy_args = init==null? List.<JCExpression>nil() : translate(List.of(init), localContext.prev);
360 //build a sam instance using an indy call to the meta-factory
361 result = makeMetaFactoryIndyCall(tree, localContext.needsAltMetafactory(), localContext.isSerializable(), localContext.referenceKind(), refSym, indy_args);
362 }
364 /**
365 * Translate identifiers within a lambda to the mapped identifier
366 * @param tree
367 */
368 @Override
369 public void visitIdent(JCIdent tree) {
370 if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) {
371 super.visitIdent(tree);
372 } else {
373 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context;
374 if (lambdaContext.getSymbolMap(PARAM).containsKey(tree.sym)) {
375 Symbol translatedSym = lambdaContext.getSymbolMap(PARAM).get(tree.sym);
376 result = make.Ident(translatedSym).setType(tree.type);
377 } else if (lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
378 Symbol translatedSym = lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym);
379 result = make.Ident(translatedSym).setType(tree.type);
380 } else if (lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
381 Symbol translatedSym = lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
382 result = make.Ident(translatedSym).setType(translatedSym.type);
383 } else if (lambdaContext.getSymbolMap(CAPTURED_VAR).containsKey(tree.sym)) {
384 Symbol translatedSym = lambdaContext.getSymbolMap(CAPTURED_VAR).get(tree.sym);
385 result = make.Ident(translatedSym).setType(tree.type);
386 } else {
387 //access to untranslated symbols (i.e. compile-time constants,
388 //members defined inside the lambda body, etc.) )
389 super.visitIdent(tree);
390 }
391 }
392 }
394 @Override
395 public void visitVarDef(JCVariableDecl tree) {
396 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context;
397 if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
398 JCExpression init = translate(tree.init);
399 result = make.VarDef((VarSymbol)lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym), init);
400 } else if (context != null && lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
401 JCExpression init = translate(tree.init);
402 VarSymbol xsym = (VarSymbol)lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
403 result = make.VarDef(xsym, init);
404 // Replace the entered symbol for this variable
405 Scope sc = tree.sym.owner.members();
406 if (sc != null) {
407 sc.remove(tree.sym);
408 sc.enter(xsym);
409 }
410 } else {
411 super.visitVarDef(tree);
412 }
413 }
415 // </editor-fold>
417 // <editor-fold defaultstate="collapsed" desc="Translation helper methods">
419 private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) {
420 return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ?
421 makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) :
422 makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally);
423 }
425 private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) {
426 Type restype = lambdaMethodDecl.type.getReturnType();
427 boolean isLambda_void = expr.type.hasTag(VOID);
428 boolean isTarget_void = restype.hasTag(VOID);
429 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
430 if (isTarget_void) {
431 //target is void:
432 // BODY;
433 JCStatement stat = make.Exec(expr);
434 return make.Block(0, List.<JCStatement>of(stat));
435 } else if (isLambda_void && isTarget_Void) {
436 //void to Void conversion:
437 // BODY; return null;
438 ListBuffer<JCStatement> stats = ListBuffer.lb();
439 stats.append(make.Exec(expr));
440 stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
441 return make.Block(0, stats.toList());
442 } else {
443 //non-void to non-void conversion:
444 // return (TYPE)BODY;
445 JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype);
446 return make.Block(0, List.<JCStatement>of(make.Return(retExpr)));
447 }
448 }
450 private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) {
451 final Type restype = lambdaMethodDecl.type.getReturnType();
452 final boolean isTarget_void = restype.hasTag(VOID);
453 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
455 class LambdaBodyTranslator extends TreeTranslator {
457 @Override
458 public void visitClassDef(JCClassDecl tree) {
459 //do NOT recurse on any inner classes
460 result = tree;
461 }
463 @Override
464 public void visitLambda(JCLambda tree) {
465 //do NOT recurse on any nested lambdas
466 result = tree;
467 }
469 @Override
470 public void visitReturn(JCReturn tree) {
471 boolean isLambda_void = tree.expr == null;
472 if (isTarget_void && !isLambda_void) {
473 //Void to void conversion:
474 // { TYPE $loc = RET-EXPR; return; }
475 VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym);
476 JCVariableDecl varDef = make.VarDef(loc, tree.expr);
477 result = make.Block(0, List.<JCStatement>of(varDef, make.Return(null)));
478 } else if (!isTarget_void || !isLambda_void) {
479 //non-void to non-void conversion:
480 // return (TYPE)RET-EXPR;
481 tree.expr = transTypes.coerce(attrEnv, tree.expr, restype);
482 result = tree;
483 } else {
484 result = tree;
485 }
487 }
488 }
490 JCBlock trans_block = new LambdaBodyTranslator().translate(block);
491 if (completeNormally && isTarget_Void) {
492 //there's no return statement and the lambda (possibly inferred)
493 //return type is java.lang.Void; emit a synthetic return statement
494 trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
495 }
496 return trans_block;
497 }
499 private JCMethodDecl makeDeserializeMethod(Symbol kSym) {
500 ListBuffer<JCCase> cases = ListBuffer.lb();
501 ListBuffer<JCBreak> breaks = ListBuffer.lb();
502 for (Map.Entry<String, ListBuffer<JCStatement>> entry : kInfo.deserializeCases.entrySet()) {
503 JCBreak br = make.Break(null);
504 breaks.add(br);
505 List<JCStatement> stmts = entry.getValue().append(br).toList();
506 cases.add(make.Case(make.Literal(entry.getKey()), stmts));
507 }
508 JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
509 for (JCBreak br : breaks) {
510 br.target = sw;
511 }
512 JCBlock body = make.Block(0L, List.<JCStatement>of(
513 sw,
514 make.Throw(makeNewClass(
515 syms.illegalArgumentExceptionType,
516 List.<JCExpression>of(make.Literal("Invalid lambda deserialization"))))));
517 JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()),
518 names.deserializeLambda,
519 make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym),
520 List.<JCTypeParameter>nil(),
521 List.of(make.VarDef(kInfo.deserParamSym, null)),
522 List.<JCExpression>nil(),
523 body,
524 null);
525 deser.sym = kInfo.deserMethodSym;
526 deser.type = kInfo.deserMethodSym.type;
527 //System.err.printf("DESER: '%s'\n", deser);
528 return deser;
529 }
531 /** Make an attributed class instance creation expression.
532 * @param ctype The class type.
533 * @param args The constructor arguments.
534 */
535 JCNewClass makeNewClass(Type ctype, List<JCExpression> args) {
536 JCNewClass tree = make.NewClass(null,
537 null, make.QualIdent(ctype.tsym), args, null);
538 tree.constructor = rs.resolveConstructor(
539 null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil());
540 tree.type = ctype;
541 return tree;
542 }
544 private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym,
545 DiagnosticPosition pos, List<Object> staticArgs, MethodType indyType) {
546 String functionalInterfaceClass = classSig(targetType);
547 String functionalInterfaceMethodName = samSym.getSimpleName().toString();
548 String functionalInterfaceMethodSignature = methodSig(types.erasure(samSym.type));
549 String implClass = classSig(types.erasure(refSym.owner.type));
550 String implMethodName = refSym.getQualifiedName().toString();
551 String implMethodSignature = methodSig(types.erasure(refSym.type));
553 JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType), make.Literal(implMethodKind));
554 ListBuffer<JCExpression> serArgs = ListBuffer.lb();
555 int i = 0;
556 for (Type t : indyType.getParameterTypes()) {
557 List<JCExpression> indexAsArg = ListBuffer.<JCExpression>lb().append(make.Literal(i)).toList();
558 List<Type> argTypes = ListBuffer.<Type>lb().append(syms.intType).toList();
559 serArgs.add(make.TypeCast(types.erasure(t), deserGetter("getCapturedArg", syms.objectType, argTypes, indexAsArg)));
560 ++i;
561 }
562 JCStatement stmt = make.If(
563 deserTest(deserTest(deserTest(deserTest(deserTest(
564 kindTest,
565 "getFunctionalInterfaceClass", functionalInterfaceClass),
566 "getFunctionalInterfaceMethodName", functionalInterfaceMethodName),
567 "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature),
568 "getImplClass", implClass),
569 "getImplMethodSignature", implMethodSignature),
570 make.Return(makeIndyCall(
571 pos,
572 syms.lambdaMetafactory,
573 names.altMetaFactory,
574 staticArgs, indyType, serArgs.toList())),
575 null);
576 ListBuffer<JCStatement> stmts = kInfo.deserializeCases.get(implMethodName);
577 if (stmts == null) {
578 stmts = ListBuffer.lb();
579 kInfo.deserializeCases.put(implMethodName, stmts);
580 }
581 /****
582 System.err.printf("+++++++++++++++++\n");
583 System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass);
584 System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName);
585 System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature);
586 System.err.printf("*implMethodKind: %d\n", implMethodKind);
587 System.err.printf("*implClass: '%s'\n", implClass);
588 System.err.printf("*implMethodName: '%s'\n", implMethodName);
589 System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature);
590 ****/
591 stmts.append(stmt);
592 }
594 private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) {
595 JCBinary testExpr = make.Binary(JCTree.Tag.EQ, arg1, arg2);
596 testExpr.operator = rs.resolveBinaryOperator(null, JCTree.Tag.EQ, attrEnv, argType, argType);
597 testExpr.setType(syms.booleanType);
598 return testExpr;
599 }
601 private JCExpression deserTest(JCExpression prev, String func, String lit) {
602 MethodType eqmt = new MethodType(List.of(syms.objectType), syms.booleanType, List.<Type>nil(), syms.methodClass);
603 Symbol eqsym = rs.resolveQualifiedMethod(null, attrEnv, syms.objectType, names.equals, List.of(syms.objectType), List.<Type>nil());
604 JCMethodInvocation eqtest = make.Apply(
605 List.<JCExpression>nil(),
606 make.Select(deserGetter(func, syms.stringType), eqsym).setType(eqmt),
607 List.<JCExpression>of(make.Literal(lit)));
608 eqtest.setType(syms.booleanType);
609 JCBinary compound = make.Binary(JCTree.Tag.AND, prev, eqtest);
610 compound.operator = rs.resolveBinaryOperator(null, JCTree.Tag.AND, attrEnv, syms.booleanType, syms.booleanType);
611 compound.setType(syms.booleanType);
612 return compound;
613 }
615 private JCExpression deserGetter(String func, Type type) {
616 return deserGetter(func, type, List.<Type>nil(), List.<JCExpression>nil());
617 }
619 private JCExpression deserGetter(String func, Type type, List<Type> argTypes, List<JCExpression> args) {
620 MethodType getmt = new MethodType(argTypes, type, List.<Type>nil(), syms.methodClass);
621 Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.<Type>nil());
622 return make.Apply(
623 List.<JCExpression>nil(),
624 make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt),
625 args).setType(type);
626 }
628 /**
629 * Create new synthetic method with given flags, name, type, owner
630 */
631 private MethodSymbol makeSyntheticMethod(long flags, Name name, Type type, Symbol owner) {
632 return new MethodSymbol(flags | SYNTHETIC, name, type, owner);
633 }
635 /**
636 * Create new synthetic variable with given flags, name, type, owner
637 */
638 private VarSymbol makeSyntheticVar(long flags, String name, Type type, Symbol owner) {
639 return makeSyntheticVar(flags, names.fromString(name), type, owner);
640 }
642 /**
643 * Create new synthetic variable with given flags, name, type, owner
644 */
645 private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) {
646 return new VarSymbol(flags | SYNTHETIC, name, type, owner);
647 }
649 /**
650 * Set varargsElement field on a given tree (must be either a new class tree
651 * or a method call tree)
652 */
653 private void setVarargsIfNeeded(JCTree tree, Type varargsElement) {
654 if (varargsElement != null) {
655 switch (tree.getTag()) {
656 case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break;
657 case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break;
658 default: throw new AssertionError();
659 }
660 }
661 }
663 /**
664 * Convert method/constructor arguments by inserting appropriate cast
665 * as required by type-erasure - this is needed when bridging a lambda/method
666 * reference, as the bridged signature might require downcast to be compatible
667 * with the generated signature.
668 */
669 private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) {
670 Assert.check(meth.kind == Kinds.MTH);
671 List<Type> formals = types.erasure(meth.type).getParameterTypes();
672 if (varargsElement != null) {
673 Assert.check((meth.flags() & VARARGS) != 0);
674 }
675 return transTypes.translateArgs(args, formals, varargsElement, attrEnv);
676 }
678 // </editor-fold>
680 /**
681 * Generate an adapter method "bridge" for a method reference which cannot
682 * be used directly.
683 */
684 private class MemberReferenceBridger {
686 private final JCMemberReference tree;
687 private final ReferenceTranslationContext localContext;
688 private final ListBuffer<JCExpression> args = ListBuffer.lb();
689 private final ListBuffer<JCVariableDecl> params = ListBuffer.lb();
691 MemberReferenceBridger(JCMemberReference tree, ReferenceTranslationContext localContext) {
692 this.tree = tree;
693 this.localContext = localContext;
694 }
696 /**
697 * Generate the bridge
698 */
699 JCMethodDecl bridge() {
700 int prevPos = make.pos;
701 try {
702 make.at(tree);
703 Type samDesc = localContext.bridgedRefSig();
704 List<Type> samPTypes = samDesc.getParameterTypes();
706 //an extra argument is prepended to the signature of the bridge in case
707 //the member reference is an instance method reference (in which case
708 //the receiver expression is passed to the bridge itself).
709 Type recType = null;
710 switch (tree.kind) {
711 case IMPLICIT_INNER:
712 recType = tree.sym.owner.type.getEnclosingType();
713 break;
714 case BOUND:
715 recType = tree.getQualifierExpression().type;
716 break;
717 case UNBOUND:
718 recType = samPTypes.head;
719 samPTypes = samPTypes.tail;
720 break;
721 }
723 //generate the parameter list for the bridged member reference - the
724 //bridge signature will match the signature of the target sam descriptor
726 VarSymbol rcvr = (recType == null)
727 ? null
728 : addParameter("rec$", recType, false);
730 List<Type> refPTypes = tree.sym.type.getParameterTypes();
731 int refSize = refPTypes.size();
732 int samSize = samPTypes.size();
733 // Last parameter to copy from referenced method
734 int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize;
736 List<Type> l = refPTypes;
737 // Use parameter types of the referenced method, excluding final var args
738 for (int i = 0; l.nonEmpty() && i < last; ++i) {
739 addParameter("x$" + i, l.head, true);
740 l = l.tail;
741 }
742 // Flatten out the var args
743 for (int i = last; i < samSize; ++i) {
744 addParameter("xva$" + i, tree.varargsElement, true);
745 }
747 //generate the bridge method declaration
748 JCMethodDecl bridgeDecl = make.MethodDef(make.Modifiers(localContext.bridgeSym.flags()),
749 localContext.bridgeSym.name,
750 make.QualIdent(samDesc.getReturnType().tsym),
751 List.<JCTypeParameter>nil(),
752 params.toList(),
753 tree.sym.type.getThrownTypes() == null
754 ? List.<JCExpression>nil()
755 : make.Types(tree.sym.type.getThrownTypes()),
756 null,
757 null);
758 bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym;
759 bridgeDecl.type = localContext.bridgeSym.type =
760 types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
762 //bridge method body generation - this can be either a method call or a
763 //new instance creation expression, depending on the member reference kind
764 JCExpression bridgeExpr = (tree.getMode() == ReferenceMode.INVOKE)
765 ? bridgeExpressionInvoke(makeReceiver(rcvr))
766 : bridgeExpressionNew();
768 //the body is either a return expression containing a method call,
769 //or the method call itself, depending on whether the return type of
770 //the bridge is non-void/void.
771 bridgeDecl.body = makeLambdaExpressionBody(bridgeExpr, bridgeDecl);
773 return bridgeDecl;
774 } finally {
775 make.at(prevPos);
776 }
777 }
778 //where
779 private JCExpression makeReceiver(VarSymbol rcvr) {
780 if (rcvr == null) return null;
781 JCExpression rcvrExpr = make.Ident(rcvr);
782 Type rcvrType = tree.sym.enclClass().type;
783 if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) {
784 rcvrExpr = make.TypeCast(make.Type(rcvrType), rcvrExpr).setType(rcvrType);
785 }
786 return rcvrExpr;
787 }
789 /**
790 * determine the receiver of the bridged method call - the receiver can
791 * be either the synthetic receiver parameter or a type qualifier; the
792 * original qualifier expression is never used here, as it might refer
793 * to symbols not available in the static context of the bridge
794 */
795 private JCExpression bridgeExpressionInvoke(JCExpression rcvr) {
796 JCExpression qualifier =
797 tree.sym.isStatic() ?
798 make.Type(tree.sym.owner.type) :
799 (rcvr != null) ?
800 rcvr :
801 tree.getQualifierExpression();
803 //create the qualifier expression
804 JCFieldAccess select = make.Select(qualifier, tree.sym.name);
805 select.sym = tree.sym;
806 select.type = tree.sym.erasure(types);
808 //create the method call expression
809 JCExpression apply = make.Apply(List.<JCExpression>nil(), select,
810 convertArgs(tree.sym, args.toList(), tree.varargsElement)).
811 setType(tree.sym.erasure(types).getReturnType());
813 apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType());
814 setVarargsIfNeeded(apply, tree.varargsElement);
815 return apply;
816 }
818 /**
819 * the enclosing expression is either 'null' (no enclosing type) or set
820 * to the first bridge synthetic parameter
821 */
822 private JCExpression bridgeExpressionNew() {
823 if (tree.kind == ReferenceKind.ARRAY_CTOR) {
824 //create the array creation expression
825 JCNewArray newArr = make.NewArray(
826 make.Type(types.elemtype(tree.getQualifierExpression().type)),
827 List.of(make.Ident(params.first())),
828 null);
829 newArr.type = tree.getQualifierExpression().type;
830 return newArr;
831 } else {
832 JCExpression encl = null;
833 switch (tree.kind) {
834 case UNBOUND:
835 case IMPLICIT_INNER:
836 encl = make.Ident(params.first());
837 }
839 //create the instance creation expression
840 JCNewClass newClass = make.NewClass(encl,
841 List.<JCExpression>nil(),
842 make.Type(tree.getQualifierExpression().type),
843 convertArgs(tree.sym, args.toList(), tree.varargsElement),
844 null);
845 newClass.constructor = tree.sym;
846 newClass.constructorType = tree.sym.erasure(types);
847 newClass.type = tree.getQualifierExpression().type;
848 setVarargsIfNeeded(newClass, tree.varargsElement);
849 return newClass;
850 }
851 }
853 private VarSymbol addParameter(String name, Type p, boolean genArg) {
854 VarSymbol vsym = new VarSymbol(0, names.fromString(name), p, localContext.bridgeSym);
855 params.append(make.VarDef(vsym, null));
856 if (genArg) {
857 args.append(make.Ident(vsym));
858 }
859 return vsym;
860 }
861 }
863 /**
864 * Bridges a member reference - this is needed when:
865 * * Var args in the referenced method need to be flattened away
866 * * super is used
867 */
868 private void bridgeMemberReference(JCMemberReference tree, ReferenceTranslationContext localContext) {
869 kInfo.addMethod(new MemberReferenceBridger(tree, localContext).bridge());
870 }
872 /**
873 * Generate an indy method call to the meta factory
874 */
875 private JCExpression makeMetaFactoryIndyCall(JCFunctionalExpression tree, boolean needsAltMetafactory,
876 boolean isSerializable, int refKind, Symbol refSym, List<JCExpression> indy_args) {
877 //determine the static bsm args
878 Type mtype = types.erasure(tree.descriptorType);
879 MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
880 List<Object> staticArgs = List.<Object>of(
881 new Pool.MethodHandle(ClassFile.REF_invokeInterface,
882 types.findDescriptorSymbol(tree.type.tsym), types),
883 new Pool.MethodHandle(refKind, refSym, types),
884 new MethodType(mtype.getParameterTypes(),
885 mtype.getReturnType(),
886 mtype.getThrownTypes(),
887 syms.methodClass));
889 //computed indy arg types
890 ListBuffer<Type> indy_args_types = ListBuffer.lb();
891 for (JCExpression arg : indy_args) {
892 indy_args_types.append(arg.type);
893 }
895 //finally, compute the type of the indy call
896 MethodType indyType = new MethodType(indy_args_types.toList(),
897 tree.type,
898 List.<Type>nil(),
899 syms.methodClass);
901 Name metafactoryName = needsAltMetafactory ?
902 names.altMetaFactory : names.metaFactory;
904 if (needsAltMetafactory) {
905 ListBuffer<Object> markers = ListBuffer.lb();
906 for (Symbol t : tree.targets.tail) {
907 if (t != syms.serializableType.tsym) {
908 markers.append(t);
909 }
910 }
911 int flags = isSerializable? FLAG_SERIALIZABLE : 0;
912 boolean hasMarkers = markers.nonEmpty();
913 flags |= hasMarkers ? FLAG_MARKERS : 0;
914 staticArgs = staticArgs.append(flags);
915 if (hasMarkers) {
916 staticArgs = staticArgs.append(markers.length());
917 staticArgs = staticArgs.appendList(markers.toList());
918 }
919 if (isSerializable) {
920 addDeserializationCase(refKind, refSym, tree.type, samSym,
921 tree, staticArgs, indyType);
922 }
923 }
925 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args);
926 }
928 /**
929 * Generate an indy method call with given name, type and static bootstrap
930 * arguments types
931 */
932 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
933 List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs) {
934 int prevPos = make.pos;
935 try {
936 make.at(pos);
937 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
938 syms.stringType,
939 syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs));
941 Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site,
942 bsmName, bsm_staticArgs, List.<Type>nil());
944 DynamicMethodSymbol dynSym =
945 new DynamicMethodSymbol(names.lambda,
946 syms.noSymbol,
947 bsm.isStatic() ?
948 ClassFile.REF_invokeStatic :
949 ClassFile.REF_invokeVirtual,
950 (MethodSymbol)bsm,
951 indyType,
952 staticArgs.toArray());
954 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName);
955 qualifier.sym = dynSym;
956 qualifier.type = indyType.getReturnType();
958 JCMethodInvocation proxyCall = make.Apply(List.<JCExpression>nil(), qualifier, indyArgs);
959 proxyCall.type = indyType.getReturnType();
960 return proxyCall;
961 } finally {
962 make.at(prevPos);
963 }
964 }
965 //where
966 private List<Type> bsmStaticArgToTypes(List<Object> args) {
967 ListBuffer<Type> argtypes = ListBuffer.lb();
968 for (Object arg : args) {
969 argtypes.append(bsmStaticArgToType(arg));
970 }
971 return argtypes.toList();
972 }
974 private Type bsmStaticArgToType(Object arg) {
975 Assert.checkNonNull(arg);
976 if (arg instanceof ClassSymbol) {
977 return syms.classType;
978 } else if (arg instanceof Integer) {
979 return syms.intType;
980 } else if (arg instanceof Long) {
981 return syms.longType;
982 } else if (arg instanceof Float) {
983 return syms.floatType;
984 } else if (arg instanceof Double) {
985 return syms.doubleType;
986 } else if (arg instanceof String) {
987 return syms.stringType;
988 } else if (arg instanceof Pool.MethodHandle) {
989 return syms.methodHandleType;
990 } else if (arg instanceof MethodType) {
991 return syms.methodTypeType;
992 } else {
993 Assert.error("bad static arg " + arg.getClass());
994 return null;
995 }
996 }
998 /**
999 * Get the opcode associated with this method reference
1000 */
1001 private int referenceKind(Symbol refSym) {
1002 if (refSym.isConstructor()) {
1003 return ClassFile.REF_newInvokeSpecial;
1004 } else {
1005 if (refSym.isStatic()) {
1006 return ClassFile.REF_invokeStatic;
1007 } else if (refSym.enclClass().isInterface()) {
1008 return ClassFile.REF_invokeInterface;
1009 } else {
1010 return (refSym.flags() & PRIVATE) != 0 ?
1011 ClassFile.REF_invokeSpecial :
1012 ClassFile.REF_invokeVirtual;
1013 }
1014 }
1015 }
1017 // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer">
1018 /**
1019 * This visitor collects information about translation of a lambda expression.
1020 * More specifically, it keeps track of the enclosing contexts and captured locals
1021 * accessed by the lambda being translated (as well as other useful info).
1022 */
1023 class LambdaAnalyzer extends TreeScanner {
1025 /** the frame stack - used to reconstruct translation info about enclosing scopes */
1026 private List<Frame> frameStack;
1028 /**
1029 * keep the count of lambda expression (used to generate unambiguous
1030 * names)
1031 */
1032 private int lambdaCount = 0;
1034 /**
1035 * keep the count of lambda expression defined in given context (used to
1036 * generate unambiguous names for serializable lambdas)
1037 */
1038 private Map<String, Integer> serializableLambdaCounts =
1039 new HashMap<String, Integer>();
1041 private Map<Symbol, JCClassDecl> localClassDefs;
1043 /**
1044 * maps for fake clinit symbols to be used as owners of lambda occurring in
1045 * a static var init context
1046 */
1047 private Map<ClassSymbol, Symbol> clinits =
1048 new HashMap<ClassSymbol, Symbol>();
1050 private void analyzeClass(JCClassDecl tree) {
1051 frameStack = List.nil();
1052 localClassDefs = new HashMap<Symbol, JCClassDecl>();
1053 scan(tree);
1054 }
1056 @Override
1057 public void visitBlock(JCBlock tree) {
1058 List<Frame> prevStack = frameStack;
1059 try {
1060 if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) {
1061 frameStack = frameStack.prepend(new Frame(tree));
1062 }
1063 super.visitBlock(tree);
1064 }
1065 finally {
1066 frameStack = prevStack;
1067 }
1068 }
1070 @Override
1071 public void visitClassDef(JCClassDecl tree) {
1072 List<Frame> prevStack = frameStack;
1073 Map<String, Integer> prevSerializableLambdaCount =
1074 serializableLambdaCounts;
1075 Map<ClassSymbol, Symbol> prevClinits = clinits;
1076 try {
1077 serializableLambdaCounts = new HashMap<String, Integer>();
1078 prevClinits = new HashMap<ClassSymbol, Symbol>();
1079 if (tree.sym.owner.kind == MTH) {
1080 localClassDefs.put(tree.sym, tree);
1081 }
1082 if (directlyEnclosingLambda() != null) {
1083 tree.sym.owner = owner();
1084 if (tree.sym.hasOuterInstance()) {
1085 //if a class is defined within a lambda, the lambda must capture
1086 //its enclosing instance (if any)
1087 TranslationContext<?> localContext = context();
1088 while (localContext != null) {
1089 if (localContext.tree.getTag() == LAMBDA) {
1090 ((LambdaTranslationContext)localContext)
1091 .addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS);
1092 }
1093 localContext = localContext.prev;
1094 }
1095 }
1096 }
1097 frameStack = frameStack.prepend(new Frame(tree));
1098 super.visitClassDef(tree);
1099 }
1100 finally {
1101 frameStack = prevStack;
1102 serializableLambdaCounts = prevSerializableLambdaCount;
1103 clinits = prevClinits;
1104 }
1105 }
1107 @Override
1108 public void visitIdent(JCIdent tree) {
1109 if (context() != null && lambdaIdentSymbolFilter(tree.sym)) {
1110 if (tree.sym.kind == VAR &&
1111 tree.sym.owner.kind == MTH &&
1112 tree.type.constValue() == null) {
1113 TranslationContext<?> localContext = context();
1114 while (localContext != null) {
1115 if (localContext.tree.getTag() == LAMBDA) {
1116 JCTree block = capturedDecl(localContext.depth, tree.sym);
1117 if (block == null) break;
1118 ((LambdaTranslationContext)localContext)
1119 .addSymbol(tree.sym, CAPTURED_VAR);
1120 }
1121 localContext = localContext.prev;
1122 }
1123 } else if (tree.sym.owner.kind == TYP) {
1124 TranslationContext<?> localContext = context();
1125 while (localContext != null) {
1126 if (localContext.tree.hasTag(LAMBDA)) {
1127 JCTree block = capturedDecl(localContext.depth, tree.sym);
1128 if (block == null) break;
1129 switch (block.getTag()) {
1130 case CLASSDEF:
1131 JCClassDecl cdecl = (JCClassDecl)block;
1132 ((LambdaTranslationContext)localContext)
1133 .addSymbol(cdecl.sym, CAPTURED_THIS);
1134 break;
1135 default:
1136 Assert.error("bad block kind");
1137 }
1138 }
1139 localContext = localContext.prev;
1140 }
1141 }
1142 }
1143 super.visitIdent(tree);
1144 }
1146 @Override
1147 public void visitLambda(JCLambda tree) {
1148 List<Frame> prevStack = frameStack;
1149 try {
1150 LambdaTranslationContext context = (LambdaTranslationContext)makeLambdaContext(tree);
1151 frameStack = frameStack.prepend(new Frame(tree));
1152 for (JCVariableDecl param : tree.params) {
1153 context.addSymbol(param.sym, PARAM);
1154 frameStack.head.addLocal(param.sym);
1155 }
1156 contextMap.put(tree, context);
1157 scan(tree.body);
1158 context.complete();
1159 }
1160 finally {
1161 frameStack = prevStack;
1162 }
1163 }
1165 @Override
1166 public void visitMethodDef(JCMethodDecl tree) {
1167 List<Frame> prevStack = frameStack;
1168 try {
1169 frameStack = frameStack.prepend(new Frame(tree));
1170 super.visitMethodDef(tree);
1171 }
1172 finally {
1173 frameStack = prevStack;
1174 }
1175 }
1177 @Override
1178 public void visitNewClass(JCNewClass tree) {
1179 if (lambdaNewClassFilter(context(), tree)) {
1180 TranslationContext<?> localContext = context();
1181 while (localContext != null) {
1182 if (localContext.tree.getTag() == LAMBDA) {
1183 ((LambdaTranslationContext)localContext)
1184 .addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS);
1185 }
1186 localContext = localContext.prev;
1187 }
1188 }
1189 if (context() != null && tree.type.tsym.owner.kind == MTH) {
1190 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context();
1191 captureLocalClassDefs(tree.type.tsym, lambdaContext);
1192 }
1193 super.visitNewClass(tree);
1194 }
1195 //where
1196 void captureLocalClassDefs(Symbol csym, final LambdaTranslationContext lambdaContext) {
1197 JCClassDecl localCDef = localClassDefs.get(csym);
1198 if (localCDef != null && localCDef.pos < lambdaContext.tree.pos) {
1199 BasicFreeVarCollector fvc = lower.new BasicFreeVarCollector() {
1200 @Override
1201 void addFreeVars(ClassSymbol c) {
1202 captureLocalClassDefs(c, lambdaContext);
1203 }
1204 @Override
1205 void visitSymbol(Symbol sym) {
1206 if (sym.kind == VAR &&
1207 sym.owner.kind == MTH &&
1208 ((VarSymbol)sym).getConstValue() == null) {
1209 TranslationContext<?> localContext = context();
1210 while (localContext != null) {
1211 if (localContext.tree.getTag() == LAMBDA) {
1212 JCTree block = capturedDecl(localContext.depth, sym);
1213 if (block == null) break;
1214 ((LambdaTranslationContext)localContext).addSymbol(sym, CAPTURED_VAR);
1215 }
1216 localContext = localContext.prev;
1217 }
1218 }
1219 }
1220 };
1221 fvc.scan(localCDef);
1222 }
1223 }
1225 @Override
1226 public void visitReference(JCMemberReference tree) {
1227 scan(tree.getQualifierExpression());
1228 contextMap.put(tree, makeReferenceContext(tree));
1229 }
1231 @Override
1232 public void visitSelect(JCFieldAccess tree) {
1233 if (context() != null && lambdaSelectSymbolFilter(tree.sym)) {
1234 TranslationContext<?> localContext = context();
1235 while (localContext != null) {
1236 if (localContext.tree.hasTag(LAMBDA)) {
1237 JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym);
1238 if (clazz == null) break;
1239 ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS);
1240 }
1241 localContext = localContext.prev;
1242 }
1243 scan(tree.selected);
1244 } else {
1245 super.visitSelect(tree);
1246 }
1247 }
1249 @Override
1250 public void visitVarDef(JCVariableDecl tree) {
1251 TranslationContext<?> context = context();
1252 LambdaTranslationContext ltc = (context != null && context instanceof LambdaTranslationContext)?
1253 (LambdaTranslationContext)context :
1254 null;
1255 if (ltc != null) {
1256 if (frameStack.head.tree.hasTag(LAMBDA)) {
1257 ltc.addSymbol(tree.sym, LOCAL_VAR);
1258 }
1259 // Check for type variables (including as type arguments).
1260 // If they occur within class nested in a lambda, mark for erasure
1261 Type type = tree.sym.asType();
1262 if (inClassWithinLambda() && !types.isSameType(types.erasure(type), type)) {
1263 ltc.addSymbol(tree.sym, TYPE_VAR);
1264 }
1265 }
1267 List<Frame> prevStack = frameStack;
1268 try {
1269 if (tree.sym.owner.kind == MTH) {
1270 frameStack.head.addLocal(tree.sym);
1271 }
1272 frameStack = frameStack.prepend(new Frame(tree));
1273 super.visitVarDef(tree);
1274 }
1275 finally {
1276 frameStack = prevStack;
1277 }
1278 }
1280 private Name lambdaName() {
1281 return names.lambda.append(names.fromString("" + lambdaCount++));
1282 }
1284 /**
1285 * For a serializable lambda, generate a name which maximizes name
1286 * stability across deserialization.
1287 * @param owner
1288 * @return Name to use for the synthetic lambda method name
1289 */
1290 private Name serializedLambdaName(Symbol owner) {
1291 StringBuilder buf = new StringBuilder();
1292 buf.append(names.lambda);
1293 // Append the name of the method enclosing the lambda.
1294 String methodName = owner.name.toString();
1295 if (methodName.equals("<clinit>"))
1296 methodName = "static";
1297 else if (methodName.equals("<init>"))
1298 methodName = "new";
1299 buf.append(methodName);
1300 buf.append('$');
1301 // Append a hash of the enclosing method signature to differentiate
1302 // overloaded enclosing methods. For lambdas enclosed in lambdas,
1303 // the generated lambda method will not have type yet, but the
1304 // enclosing method's name will have been generated with this same
1305 // method, so it will be unique and never be overloaded.
1306 Assert.check(owner.type != null || directlyEnclosingLambda() != null);
1307 if (owner.type != null) {
1308 int methTypeHash = methodSig(owner.type).hashCode();
1309 buf.append(Integer.toHexString(methTypeHash));
1310 }
1311 buf.append('$');
1312 // The above appended name components may not be unique, append a
1313 // count based on the above name components.
1314 String temp = buf.toString();
1315 Integer count = serializableLambdaCounts.get(temp);
1316 if (count == null) {
1317 count = 0;
1318 }
1319 buf.append(count++);
1320 serializableLambdaCounts.put(temp, count);
1321 return names.fromString(buf.toString());
1322 }
1324 /**
1325 * Return a valid owner given the current declaration stack
1326 * (required to skip synthetic lambda symbols)
1327 */
1328 private Symbol owner() {
1329 return owner(false);
1330 }
1332 @SuppressWarnings("fallthrough")
1333 private Symbol owner(boolean skipLambda) {
1334 List<Frame> frameStack2 = frameStack;
1335 while (frameStack2.nonEmpty()) {
1336 switch (frameStack2.head.tree.getTag()) {
1337 case VARDEF:
1338 if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) {
1339 frameStack2 = frameStack2.tail;
1340 break;
1341 }
1342 JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree;
1343 return initSym(cdecl.sym,
1344 ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC);
1345 case BLOCK:
1346 JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree;
1347 return initSym(cdecl2.sym,
1348 ((JCBlock)frameStack2.head.tree).flags & STATIC);
1349 case CLASSDEF:
1350 return ((JCClassDecl)frameStack2.head.tree).sym;
1351 case METHODDEF:
1352 return ((JCMethodDecl)frameStack2.head.tree).sym;
1353 case LAMBDA:
1354 if (!skipLambda)
1355 return ((LambdaTranslationContext)contextMap
1356 .get(frameStack2.head.tree)).translatedSym;
1357 default:
1358 frameStack2 = frameStack2.tail;
1359 }
1360 }
1361 Assert.error();
1362 return null;
1363 }
1365 private Symbol initSym(ClassSymbol csym, long flags) {
1366 boolean isStatic = (flags & STATIC) != 0;
1367 if (isStatic) {
1368 //static clinits are generated in Gen - so we need to fake them
1369 Symbol clinit = clinits.get(csym);
1370 if (clinit == null) {
1371 clinit = makeSyntheticMethod(STATIC,
1372 names.clinit,
1373 new MethodType(List.<Type>nil(), syms.voidType, List.<Type>nil(), syms.methodClass),
1374 csym);
1375 clinits.put(csym, clinit);
1376 }
1377 return clinit;
1378 } else {
1379 //get the first constructor and treat it as the instance init sym
1380 for (Symbol s : csym.members_field.getElementsByName(names.init)) {
1381 return s;
1382 }
1383 }
1384 Assert.error("init not found");
1385 return null;
1386 }
1388 private JCTree directlyEnclosingLambda() {
1389 if (frameStack.isEmpty()) {
1390 return null;
1391 }
1392 List<Frame> frameStack2 = frameStack;
1393 while (frameStack2.nonEmpty()) {
1394 switch (frameStack2.head.tree.getTag()) {
1395 case CLASSDEF:
1396 case METHODDEF:
1397 return null;
1398 case LAMBDA:
1399 return frameStack2.head.tree;
1400 default:
1401 frameStack2 = frameStack2.tail;
1402 }
1403 }
1404 Assert.error();
1405 return null;
1406 }
1408 private boolean inClassWithinLambda() {
1409 if (frameStack.isEmpty()) {
1410 return false;
1411 }
1412 List<Frame> frameStack2 = frameStack;
1413 boolean classFound = false;
1414 while (frameStack2.nonEmpty()) {
1415 switch (frameStack2.head.tree.getTag()) {
1416 case LAMBDA:
1417 return classFound;
1418 case CLASSDEF:
1419 classFound = true;
1420 frameStack2 = frameStack2.tail;
1421 break;
1422 default:
1423 frameStack2 = frameStack2.tail;
1424 }
1425 }
1426 // No lambda
1427 return false;
1428 }
1430 /**
1431 * Return the declaration corresponding to a symbol in the enclosing
1432 * scope; the depth parameter is used to filter out symbols defined
1433 * in nested scopes (which do not need to undergo capture).
1434 */
1435 private JCTree capturedDecl(int depth, Symbol sym) {
1436 int currentDepth = frameStack.size() - 1;
1437 for (Frame block : frameStack) {
1438 switch (block.tree.getTag()) {
1439 case CLASSDEF:
1440 ClassSymbol clazz = ((JCClassDecl)block.tree).sym;
1441 if (sym.isMemberOf(clazz, types)) {
1442 return currentDepth > depth ? null : block.tree;
1443 }
1444 break;
1445 case VARDEF:
1446 if (((JCVariableDecl)block.tree).sym == sym &&
1447 sym.owner.kind == MTH) { //only locals are captured
1448 return currentDepth > depth ? null : block.tree;
1449 }
1450 break;
1451 case BLOCK:
1452 case METHODDEF:
1453 case LAMBDA:
1454 if (block.locals != null && block.locals.contains(sym)) {
1455 return currentDepth > depth ? null : block.tree;
1456 }
1457 break;
1458 default:
1459 Assert.error("bad decl kind " + block.tree.getTag());
1460 }
1461 currentDepth--;
1462 }
1463 return null;
1464 }
1466 private TranslationContext<?> context() {
1467 for (Frame frame : frameStack) {
1468 TranslationContext<?> context = contextMap.get(frame.tree);
1469 if (context != null) {
1470 return context;
1471 }
1472 }
1473 return null;
1474 }
1476 /**
1477 * This is used to filter out those identifiers that needs to be adjusted
1478 * when translating away lambda expressions
1479 */
1480 private boolean lambdaIdentSymbolFilter(Symbol sym) {
1481 return (sym.kind == VAR || sym.kind == MTH)
1482 && !sym.isStatic()
1483 && sym.name != names.init;
1484 }
1486 private boolean lambdaSelectSymbolFilter(Symbol sym) {
1487 return (sym.kind == VAR || sym.kind == MTH) &&
1488 !sym.isStatic() &&
1489 (sym.name == names._this ||
1490 sym.name == names._super);
1491 }
1493 /**
1494 * This is used to filter out those new class expressions that need to
1495 * be qualified with an enclosing tree
1496 */
1497 private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) {
1498 if (context != null
1499 && tree.encl == null
1500 && tree.def == null
1501 && !tree.type.getEnclosingType().hasTag(NONE)) {
1502 Type encl = tree.type.getEnclosingType();
1503 Type current = context.owner.enclClass().type;
1504 while (!current.hasTag(NONE)) {
1505 if (current.tsym.isSubClass(encl.tsym, types)) {
1506 return true;
1507 }
1508 current = current.getEnclosingType();
1509 }
1510 return false;
1511 } else {
1512 return false;
1513 }
1514 }
1516 private TranslationContext<JCLambda> makeLambdaContext(JCLambda tree) {
1517 return new LambdaTranslationContext(tree);
1518 }
1520 private TranslationContext<JCMemberReference> makeReferenceContext(JCMemberReference tree) {
1521 return new ReferenceTranslationContext(tree);
1522 }
1524 private class Frame {
1525 final JCTree tree;
1526 List<Symbol> locals;
1528 public Frame(JCTree tree) {
1529 this.tree = tree;
1530 }
1532 void addLocal(Symbol sym) {
1533 if (locals == null) {
1534 locals = List.nil();
1535 }
1536 locals = locals.prepend(sym);
1537 }
1538 }
1540 /**
1541 * This class is used to store important information regarding translation of
1542 * lambda expression/method references (see subclasses).
1543 */
1544 private abstract class TranslationContext<T extends JCFunctionalExpression> {
1546 /** the underlying (untranslated) tree */
1547 T tree;
1549 /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */
1550 Symbol owner;
1552 /** the depth of this lambda expression in the frame stack */
1553 int depth;
1555 /** the enclosing translation context (set for nested lambdas/mref) */
1556 TranslationContext<?> prev;
1558 TranslationContext(T tree) {
1559 this.tree = tree;
1560 this.owner = owner();
1561 this.depth = frameStack.size() - 1;
1562 this.prev = context();
1563 }
1565 /** does this functional expression need to be created using alternate metafactory? */
1566 boolean needsAltMetafactory() {
1567 return (tree.targets.length() > 1 ||
1568 isSerializable());
1569 }
1571 /** does this functional expression require serialization support? */
1572 boolean isSerializable() {
1573 for (Symbol target : tree.targets) {
1574 if (types.asSuper(target.type, syms.serializableType.tsym) != null) {
1575 return true;
1576 }
1577 }
1578 return false;
1579 }
1580 }
1582 /**
1583 * This class retains all the useful information about a lambda expression;
1584 * the contents of this class are filled by the LambdaAnalyzer visitor,
1585 * and the used by the main translation routines in order to adjust references
1586 * to captured locals/members, etc.
1587 */
1588 private class LambdaTranslationContext extends TranslationContext<JCLambda> {
1590 /** variable in the enclosing context to which this lambda is assigned */
1591 Symbol self;
1593 /** map from original to translated lambda parameters */
1594 Map<Symbol, Symbol> lambdaParams = new LinkedHashMap<Symbol, Symbol>();
1596 /** map from original to translated lambda locals */
1597 Map<Symbol, Symbol> lambdaLocals = new LinkedHashMap<Symbol, Symbol>();
1599 /** map from variables in enclosing scope to translated synthetic parameters */
1600 Map<Symbol, Symbol> capturedLocals = new LinkedHashMap<Symbol, Symbol>();
1602 /** map from class symbols to translated synthetic parameters (for captured member access) */
1603 Map<Symbol, Symbol> capturedThis = new LinkedHashMap<Symbol, Symbol>();
1605 /** map from original to translated lambda locals */
1606 Map<Symbol, Symbol> typeVars = new LinkedHashMap<Symbol, Symbol>();
1608 /** the synthetic symbol for the method hoisting the translated lambda */
1609 Symbol translatedSym;
1611 List<JCVariableDecl> syntheticParams;
1613 LambdaTranslationContext(JCLambda tree) {
1614 super(tree);
1615 Frame frame = frameStack.head;
1616 if (frame.tree.hasTag(VARDEF)) {
1617 self = ((JCVariableDecl)frame.tree).sym;
1618 }
1619 Name name = isSerializable() ? serializedLambdaName(owner) : lambdaName();
1620 this.translatedSym = makeSyntheticMethod(0, name, null, owner.enclClass());
1621 }
1623 /**
1624 * Translate a symbol of a given kind into something suitable for the
1625 * synthetic lambda body
1626 */
1627 Symbol translate(Name name, final Symbol sym, LambdaSymbolKind skind) {
1628 switch (skind) {
1629 case CAPTURED_THIS:
1630 return sym; // self represented
1631 case TYPE_VAR:
1632 // Just erase the type var
1633 return new VarSymbol(sym.flags(), name,
1634 types.erasure(sym.type), sym.owner);
1635 case CAPTURED_VAR:
1636 return new VarSymbol(SYNTHETIC | FINAL, name, types.erasure(sym.type), translatedSym) {
1637 @Override
1638 public Symbol baseSymbol() {
1639 //keep mapping with original captured symbol
1640 return sym;
1641 }
1642 };
1643 default:
1644 return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
1645 }
1646 }
1648 void addSymbol(Symbol sym, LambdaSymbolKind skind) {
1649 Map<Symbol, Symbol> transMap = null;
1650 Name preferredName;
1651 switch (skind) {
1652 case CAPTURED_THIS:
1653 transMap = capturedThis;
1654 preferredName = names.fromString("encl$" + capturedThis.size());
1655 break;
1656 case CAPTURED_VAR:
1657 transMap = capturedLocals;
1658 preferredName = names.fromString("cap$" + capturedLocals.size());
1659 break;
1660 case LOCAL_VAR:
1661 transMap = lambdaLocals;
1662 preferredName = sym.name;
1663 break;
1664 case PARAM:
1665 transMap = lambdaParams;
1666 preferredName = sym.name;
1667 break;
1668 case TYPE_VAR:
1669 transMap = typeVars;
1670 preferredName = sym.name;
1671 break;
1672 default: throw new AssertionError();
1673 }
1674 if (!transMap.containsKey(sym)) {
1675 transMap.put(sym, translate(preferredName, sym, skind));
1676 }
1677 }
1679 Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind... skinds) {
1680 LinkedHashMap<Symbol, Symbol> translationMap = new LinkedHashMap<Symbol, Symbol>();
1681 for (LambdaSymbolKind skind : skinds) {
1682 switch (skind) {
1683 case CAPTURED_THIS:
1684 translationMap.putAll(capturedThis);
1685 break;
1686 case CAPTURED_VAR:
1687 translationMap.putAll(capturedLocals);
1688 break;
1689 case LOCAL_VAR:
1690 translationMap.putAll(lambdaLocals);
1691 break;
1692 case PARAM:
1693 translationMap.putAll(lambdaParams);
1694 break;
1695 case TYPE_VAR:
1696 translationMap.putAll(typeVars);
1697 break;
1698 default: throw new AssertionError();
1699 }
1700 }
1701 return translationMap;
1702 }
1704 /**
1705 * The translatedSym is not complete/accurate until the analysis is
1706 * finished. Once the analysis is finished, the translatedSym is
1707 * "completed" -- updated with type information, access modifiers,
1708 * and full parameter list.
1709 */
1710 void complete() {
1711 if (syntheticParams != null) {
1712 return;
1713 }
1714 boolean inInterface = translatedSym.owner.isInterface();
1715 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty();
1716 boolean needInstance = thisReferenced || inInterface;
1718 // If instance access isn't needed, make it static
1719 // Interface methods much be public default methods, otherwise make it private
1720 translatedSym.flags_field = SYNTHETIC | (needInstance? 0 : STATIC) |
1721 (inInterface? PUBLIC | DEFAULT : PRIVATE);
1723 //compute synthetic params
1724 ListBuffer<JCVariableDecl> params = ListBuffer.lb();
1726 // The signature of the method is augmented with the following
1727 // synthetic parameters:
1728 //
1729 // 1) reference to enclosing contexts captured by the lambda expression
1730 // 2) enclosing locals captured by the lambda expression
1731 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR, PARAM).values()) {
1732 params.append(make.VarDef((VarSymbol) thisSym, null));
1733 }
1735 syntheticParams = params.toList();
1737 //prepend synthetic args to translated lambda method signature
1738 translatedSym.type = types.createMethodTypeWithParameters(
1739 generatedLambdaSig(),
1740 TreeInfo.types(syntheticParams));
1741 }
1743 Type generatedLambdaSig() {
1744 return types.erasure(tree.descriptorType);
1745 }
1746 }
1748 /**
1749 * This class retains all the useful information about a method reference;
1750 * the contents of this class are filled by the LambdaAnalyzer visitor,
1751 * and the used by the main translation routines in order to adjust method
1752 * references (i.e. in case a bridge is needed)
1753 */
1754 private class ReferenceTranslationContext extends TranslationContext<JCMemberReference> {
1756 final boolean isSuper;
1757 final Symbol bridgeSym;
1759 ReferenceTranslationContext(JCMemberReference tree) {
1760 super(tree);
1761 this.isSuper = tree.hasKind(ReferenceKind.SUPER);
1762 this.bridgeSym = needsBridge()
1763 ? makeSyntheticMethod(isSuper ? 0 : STATIC,
1764 lambdaName().append(names.fromString("$bridge")), null,
1765 owner.enclClass())
1766 : null;
1767 }
1769 /**
1770 * Get the opcode associated with this method reference
1771 */
1772 int referenceKind() {
1773 return LambdaToMethod.this.referenceKind(needsBridge() ? bridgeSym : tree.sym);
1774 }
1776 boolean needsVarArgsConversion() {
1777 return tree.varargsElement != null;
1778 }
1780 /**
1781 * @return Is this an array operation like clone()
1782 */
1783 boolean isArrayOp() {
1784 return tree.sym.owner == syms.arrayClass;
1785 }
1787 boolean isPrivateConstructor() {
1788 //hack needed to workaround 292 bug (8005122)
1789 //when 292 issue is fixed we should simply remove this
1790 return tree.sym.name == names.init &&
1791 (tree.sym.flags() & PRIVATE) != 0;
1792 }
1794 boolean receiverAccessible() {
1795 //hack needed to workaround 292 bug (7087658)
1796 //when 292 issue is fixed we should remove this and change the backend
1797 //code to always generate a method handle to an accessible method
1798 return tree.ownerAccessible;
1799 }
1801 /**
1802 * Does this reference needs a bridge (i.e. var args need to be
1803 * expanded or "super" is used)
1804 */
1805 final boolean needsBridge() {
1806 return isSuper || needsVarArgsConversion() || isArrayOp() ||
1807 isPrivateConstructor() || !receiverAccessible();
1808 }
1810 Type generatedRefSig() {
1811 return types.erasure(tree.sym.type);
1812 }
1814 Type bridgedRefSig() {
1815 return types.erasure(types.findDescriptorSymbol(tree.targets.head).type);
1816 }
1817 }
1818 }
1819 // </editor-fold>
1821 enum LambdaSymbolKind {
1822 CAPTURED_VAR,
1823 CAPTURED_THIS,
1824 LOCAL_VAR,
1825 PARAM,
1826 TYPE_VAR;
1827 }
1829 /**
1830 * ****************************************************************
1831 * Signature Generation
1832 * ****************************************************************
1833 */
1835 private String methodSig(Type type) {
1836 L2MSignatureGenerator sg = new L2MSignatureGenerator();
1837 sg.assembleSig(type);
1838 return sg.toString();
1839 }
1841 private String classSig(Type type) {
1842 L2MSignatureGenerator sg = new L2MSignatureGenerator();
1843 sg.assembleClassSig(type);
1844 return sg.toString();
1845 }
1847 /**
1848 * Signature Generation
1849 */
1850 private class L2MSignatureGenerator extends Types.SignatureGenerator {
1852 /**
1853 * An output buffer for type signatures.
1854 */
1855 StringBuilder sb = new StringBuilder();
1857 L2MSignatureGenerator() {
1858 super(types);
1859 }
1861 @Override
1862 protected void append(char ch) {
1863 sb.append(ch);
1864 }
1866 @Override
1867 protected void append(byte[] ba) {
1868 sb.append(new String(ba));
1869 }
1871 @Override
1872 protected void append(Name name) {
1873 sb.append(name.toString());
1874 }
1876 @Override
1877 public String toString() {
1878 return sb.toString();
1879 }
1880 }
1881 }