Tue, 12 Mar 2013 16:02:13 +0000
8008540: Constructor reference to non-reifiable array should be rejected
8008539: Spurious error when constructor reference mention an interface type
8008538: Constructor reference accepts wildcard parameterized types
Summary: Overhaul of Check.checkConstructorRefType
Reviewed-by: jjg
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 if (tree.sym.owner.kind == Kinds.TYP) {
388 for (Map.Entry<Symbol, Symbol> encl_entry : lambdaContext.getSymbolMap(CAPTURED_THIS).entrySet()) {
389 if (tree.sym.isMemberOf((ClassSymbol) encl_entry.getKey(), types)) {
390 JCExpression enclRef = make.Ident(encl_entry.getValue());
391 result = tree.sym.name == names._this
392 ? enclRef.setType(tree.type)
393 : make.Select(enclRef, tree.sym).setType(tree.type);
394 result = tree;
395 return;
396 }
397 }
398 }
399 //access to untranslated symbols (i.e. compile-time constants,
400 //members defined inside the lambda body, etc.) )
401 super.visitIdent(tree);
402 }
403 }
404 }
406 @Override
407 public void visitVarDef(JCVariableDecl tree) {
408 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context;
409 if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
410 JCExpression init = translate(tree.init);
411 result = make.VarDef((VarSymbol)lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym), init);
412 } else if (context != null && lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
413 JCExpression init = translate(tree.init);
414 VarSymbol xsym = (VarSymbol)lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
415 result = make.VarDef(xsym, init);
416 // Replace the entered symbol for this variable
417 Scope sc = tree.sym.owner.members();
418 if (sc != null) {
419 sc.remove(tree.sym);
420 sc.enter(xsym);
421 }
422 } else {
423 super.visitVarDef(tree);
424 }
425 }
427 // </editor-fold>
429 // <editor-fold defaultstate="collapsed" desc="Translation helper methods">
431 private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) {
432 return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ?
433 makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) :
434 makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally);
435 }
437 private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) {
438 Type restype = lambdaMethodDecl.type.getReturnType();
439 boolean isLambda_void = expr.type.hasTag(VOID);
440 boolean isTarget_void = restype.hasTag(VOID);
441 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
442 if (isTarget_void) {
443 //target is void:
444 // BODY;
445 JCStatement stat = make.Exec(expr);
446 return make.Block(0, List.<JCStatement>of(stat));
447 } else if (isLambda_void && isTarget_Void) {
448 //void to Void conversion:
449 // BODY; return null;
450 ListBuffer<JCStatement> stats = ListBuffer.lb();
451 stats.append(make.Exec(expr));
452 stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
453 return make.Block(0, stats.toList());
454 } else {
455 //non-void to non-void conversion:
456 // return (TYPE)BODY;
457 JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype);
458 return make.Block(0, List.<JCStatement>of(make.Return(retExpr)));
459 }
460 }
462 private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) {
463 final Type restype = lambdaMethodDecl.type.getReturnType();
464 final boolean isTarget_void = restype.hasTag(VOID);
465 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
467 class LambdaBodyTranslator extends TreeTranslator {
469 @Override
470 public void visitClassDef(JCClassDecl tree) {
471 //do NOT recurse on any inner classes
472 result = tree;
473 }
475 @Override
476 public void visitLambda(JCLambda tree) {
477 //do NOT recurse on any nested lambdas
478 result = tree;
479 }
481 @Override
482 public void visitReturn(JCReturn tree) {
483 boolean isLambda_void = tree.expr == null;
484 if (isTarget_void && !isLambda_void) {
485 //Void to void conversion:
486 // { TYPE $loc = RET-EXPR; return; }
487 VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym);
488 JCVariableDecl varDef = make.VarDef(loc, tree.expr);
489 result = make.Block(0, List.<JCStatement>of(varDef, make.Return(null)));
490 } else if (!isTarget_void || !isLambda_void) {
491 //non-void to non-void conversion:
492 // return (TYPE)RET-EXPR;
493 tree.expr = transTypes.coerce(attrEnv, tree.expr, restype);
494 result = tree;
495 } else {
496 result = tree;
497 }
499 }
500 }
502 JCBlock trans_block = new LambdaBodyTranslator().translate(block);
503 if (completeNormally && isTarget_Void) {
504 //there's no return statement and the lambda (possibly inferred)
505 //return type is java.lang.Void; emit a synthetic return statement
506 trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
507 }
508 return trans_block;
509 }
511 private JCMethodDecl makeDeserializeMethod(Symbol kSym) {
512 ListBuffer<JCCase> cases = ListBuffer.lb();
513 ListBuffer<JCBreak> breaks = ListBuffer.lb();
514 for (Map.Entry<String, ListBuffer<JCStatement>> entry : kInfo.deserializeCases.entrySet()) {
515 JCBreak br = make.Break(null);
516 breaks.add(br);
517 List<JCStatement> stmts = entry.getValue().append(br).toList();
518 cases.add(make.Case(make.Literal(entry.getKey()), stmts));
519 }
520 JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
521 for (JCBreak br : breaks) {
522 br.target = sw;
523 }
524 JCBlock body = make.Block(0L, List.<JCStatement>of(
525 sw,
526 make.Throw(makeNewClass(
527 syms.illegalArgumentExceptionType,
528 List.<JCExpression>of(make.Literal("Invalid lambda deserialization"))))));
529 JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()),
530 names.deserializeLambda,
531 make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym),
532 List.<JCTypeParameter>nil(),
533 List.of(make.VarDef(kInfo.deserParamSym, null)),
534 List.<JCExpression>nil(),
535 body,
536 null);
537 deser.sym = kInfo.deserMethodSym;
538 deser.type = kInfo.deserMethodSym.type;
539 //System.err.printf("DESER: '%s'\n", deser);
540 return deser;
541 }
543 /** Make an attributed class instance creation expression.
544 * @param ctype The class type.
545 * @param args The constructor arguments.
546 */
547 JCNewClass makeNewClass(Type ctype, List<JCExpression> args) {
548 JCNewClass tree = make.NewClass(null,
549 null, make.QualIdent(ctype.tsym), args, null);
550 tree.constructor = rs.resolveConstructor(
551 null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil());
552 tree.type = ctype;
553 return tree;
554 }
556 private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym,
557 DiagnosticPosition pos, List<Object> staticArgs, MethodType indyType) {
558 String functionalInterfaceClass = classSig(targetType);
559 String functionalInterfaceMethodName = samSym.getSimpleName().toString();
560 String functionalInterfaceMethodSignature = methodSig(types.erasure(samSym.type));
561 String implClass = classSig(types.erasure(refSym.owner.type));
562 String implMethodName = refSym.getQualifiedName().toString();
563 String implMethodSignature = methodSig(types.erasure(refSym.type));
565 JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType), make.Literal(implMethodKind));
566 ListBuffer<JCExpression> serArgs = ListBuffer.lb();
567 int i = 0;
568 for (Type t : indyType.getParameterTypes()) {
569 List<JCExpression> indexAsArg = ListBuffer.<JCExpression>lb().append(make.Literal(i)).toList();
570 List<Type> argTypes = ListBuffer.<Type>lb().append(syms.intType).toList();
571 serArgs.add(make.TypeCast(types.erasure(t), deserGetter("getCapturedArg", syms.objectType, argTypes, indexAsArg)));
572 ++i;
573 }
574 JCStatement stmt = make.If(
575 deserTest(deserTest(deserTest(deserTest(deserTest(
576 kindTest,
577 "getFunctionalInterfaceClass", functionalInterfaceClass),
578 "getFunctionalInterfaceMethodName", functionalInterfaceMethodName),
579 "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature),
580 "getImplClass", implClass),
581 "getImplMethodSignature", implMethodSignature),
582 make.Return(makeIndyCall(
583 pos,
584 syms.lambdaMetafactory,
585 names.altMetaFactory,
586 staticArgs, indyType, serArgs.toList())),
587 null);
588 ListBuffer<JCStatement> stmts = kInfo.deserializeCases.get(implMethodName);
589 if (stmts == null) {
590 stmts = ListBuffer.lb();
591 kInfo.deserializeCases.put(implMethodName, stmts);
592 }
593 /****
594 System.err.printf("+++++++++++++++++\n");
595 System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass);
596 System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName);
597 System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature);
598 System.err.printf("*implMethodKind: %d\n", implMethodKind);
599 System.err.printf("*implClass: '%s'\n", implClass);
600 System.err.printf("*implMethodName: '%s'\n", implMethodName);
601 System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature);
602 ****/
603 stmts.append(stmt);
604 }
606 private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) {
607 JCBinary testExpr = make.Binary(JCTree.Tag.EQ, arg1, arg2);
608 testExpr.operator = rs.resolveBinaryOperator(null, JCTree.Tag.EQ, attrEnv, argType, argType);
609 testExpr.setType(syms.booleanType);
610 return testExpr;
611 }
613 private JCExpression deserTest(JCExpression prev, String func, String lit) {
614 MethodType eqmt = new MethodType(List.of(syms.objectType), syms.booleanType, List.<Type>nil(), syms.methodClass);
615 Symbol eqsym = rs.resolveQualifiedMethod(null, attrEnv, syms.objectType, names.equals, List.of(syms.objectType), List.<Type>nil());
616 JCMethodInvocation eqtest = make.Apply(
617 List.<JCExpression>nil(),
618 make.Select(deserGetter(func, syms.stringType), eqsym).setType(eqmt),
619 List.<JCExpression>of(make.Literal(lit)));
620 eqtest.setType(syms.booleanType);
621 JCBinary compound = make.Binary(JCTree.Tag.AND, prev, eqtest);
622 compound.operator = rs.resolveBinaryOperator(null, JCTree.Tag.AND, attrEnv, syms.booleanType, syms.booleanType);
623 compound.setType(syms.booleanType);
624 return compound;
625 }
627 private JCExpression deserGetter(String func, Type type) {
628 return deserGetter(func, type, List.<Type>nil(), List.<JCExpression>nil());
629 }
631 private JCExpression deserGetter(String func, Type type, List<Type> argTypes, List<JCExpression> args) {
632 MethodType getmt = new MethodType(argTypes, type, List.<Type>nil(), syms.methodClass);
633 Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.<Type>nil());
634 return make.Apply(
635 List.<JCExpression>nil(),
636 make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt),
637 args).setType(type);
638 }
640 /**
641 * Create new synthetic method with given flags, name, type, owner
642 */
643 private MethodSymbol makeSyntheticMethod(long flags, Name name, Type type, Symbol owner) {
644 return new MethodSymbol(flags | SYNTHETIC, name, type, owner);
645 }
647 /**
648 * Create new synthetic variable with given flags, name, type, owner
649 */
650 private VarSymbol makeSyntheticVar(long flags, String name, Type type, Symbol owner) {
651 return makeSyntheticVar(flags, names.fromString(name), type, owner);
652 }
654 /**
655 * Create new synthetic variable with given flags, name, type, owner
656 */
657 private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) {
658 return new VarSymbol(flags | SYNTHETIC, name, type, owner);
659 }
661 /**
662 * Set varargsElement field on a given tree (must be either a new class tree
663 * or a method call tree)
664 */
665 private void setVarargsIfNeeded(JCTree tree, Type varargsElement) {
666 if (varargsElement != null) {
667 switch (tree.getTag()) {
668 case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break;
669 case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break;
670 default: throw new AssertionError();
671 }
672 }
673 }
675 /**
676 * Convert method/constructor arguments by inserting appropriate cast
677 * as required by type-erasure - this is needed when bridging a lambda/method
678 * reference, as the bridged signature might require downcast to be compatible
679 * with the generated signature.
680 */
681 private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) {
682 Assert.check(meth.kind == Kinds.MTH);
683 List<Type> formals = types.erasure(meth.type).getParameterTypes();
684 if (varargsElement != null) {
685 Assert.check((meth.flags() & VARARGS) != 0);
686 }
687 return transTypes.translateArgs(args, formals, varargsElement, attrEnv);
688 }
690 // </editor-fold>
692 /**
693 * Generate an adapter method "bridge" for a method reference which cannot
694 * be used directly.
695 */
696 private class MemberReferenceBridger {
698 private final JCMemberReference tree;
699 private final ReferenceTranslationContext localContext;
700 private final ListBuffer<JCExpression> args = ListBuffer.lb();
701 private final ListBuffer<JCVariableDecl> params = ListBuffer.lb();
703 MemberReferenceBridger(JCMemberReference tree, ReferenceTranslationContext localContext) {
704 this.tree = tree;
705 this.localContext = localContext;
706 }
708 /**
709 * Generate the bridge
710 */
711 JCMethodDecl bridge() {
712 int prevPos = make.pos;
713 try {
714 make.at(tree);
715 Type samDesc = localContext.bridgedRefSig();
716 List<Type> samPTypes = samDesc.getParameterTypes();
718 //an extra argument is prepended to the signature of the bridge in case
719 //the member reference is an instance method reference (in which case
720 //the receiver expression is passed to the bridge itself).
721 Type recType = null;
722 switch (tree.kind) {
723 case IMPLICIT_INNER:
724 recType = tree.sym.owner.type.getEnclosingType();
725 break;
726 case BOUND:
727 recType = tree.getQualifierExpression().type;
728 break;
729 case UNBOUND:
730 recType = samPTypes.head;
731 samPTypes = samPTypes.tail;
732 break;
733 }
735 //generate the parameter list for the bridged member reference - the
736 //bridge signature will match the signature of the target sam descriptor
738 VarSymbol rcvr = (recType == null)
739 ? null
740 : addParameter("rec$", recType, false);
742 List<Type> refPTypes = tree.sym.type.getParameterTypes();
743 int refSize = refPTypes.size();
744 int samSize = samPTypes.size();
745 // Last parameter to copy from referenced method
746 int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize;
748 List<Type> l = refPTypes;
749 // Use parameter types of the referenced method, excluding final var args
750 for (int i = 0; l.nonEmpty() && i < last; ++i) {
751 addParameter("x$" + i, l.head, true);
752 l = l.tail;
753 }
754 // Flatten out the var args
755 for (int i = last; i < samSize; ++i) {
756 addParameter("xva$" + i, tree.varargsElement, true);
757 }
759 //generate the bridge method declaration
760 JCMethodDecl bridgeDecl = make.MethodDef(make.Modifiers(localContext.bridgeSym.flags()),
761 localContext.bridgeSym.name,
762 make.QualIdent(samDesc.getReturnType().tsym),
763 List.<JCTypeParameter>nil(),
764 params.toList(),
765 tree.sym.type.getThrownTypes() == null
766 ? List.<JCExpression>nil()
767 : make.Types(tree.sym.type.getThrownTypes()),
768 null,
769 null);
770 bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym;
771 bridgeDecl.type = localContext.bridgeSym.type =
772 types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
774 //bridge method body generation - this can be either a method call or a
775 //new instance creation expression, depending on the member reference kind
776 JCExpression bridgeExpr = (tree.getMode() == ReferenceMode.INVOKE)
777 ? bridgeExpressionInvoke(makeReceiver(rcvr))
778 : bridgeExpressionNew();
780 //the body is either a return expression containing a method call,
781 //or the method call itself, depending on whether the return type of
782 //the bridge is non-void/void.
783 bridgeDecl.body = makeLambdaExpressionBody(bridgeExpr, bridgeDecl);
785 return bridgeDecl;
786 } finally {
787 make.at(prevPos);
788 }
789 }
790 //where
791 private JCExpression makeReceiver(VarSymbol rcvr) {
792 if (rcvr == null) return null;
793 JCExpression rcvrExpr = make.Ident(rcvr);
794 Type rcvrType = tree.sym.enclClass().type;
795 if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) {
796 rcvrExpr = make.TypeCast(make.Type(rcvrType), rcvrExpr).setType(rcvrType);
797 }
798 return rcvrExpr;
799 }
801 /**
802 * determine the receiver of the bridged method call - the receiver can
803 * be either the synthetic receiver parameter or a type qualifier; the
804 * original qualifier expression is never used here, as it might refer
805 * to symbols not available in the static context of the bridge
806 */
807 private JCExpression bridgeExpressionInvoke(JCExpression rcvr) {
808 JCExpression qualifier =
809 tree.sym.isStatic() ?
810 make.Type(tree.sym.owner.type) :
811 (rcvr != null) ?
812 rcvr :
813 tree.getQualifierExpression();
815 //create the qualifier expression
816 JCFieldAccess select = make.Select(qualifier, tree.sym.name);
817 select.sym = tree.sym;
818 select.type = tree.sym.erasure(types);
820 //create the method call expression
821 JCExpression apply = make.Apply(List.<JCExpression>nil(), select,
822 convertArgs(tree.sym, args.toList(), tree.varargsElement)).
823 setType(tree.sym.erasure(types).getReturnType());
825 apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType());
826 setVarargsIfNeeded(apply, tree.varargsElement);
827 return apply;
828 }
830 /**
831 * the enclosing expression is either 'null' (no enclosing type) or set
832 * to the first bridge synthetic parameter
833 */
834 private JCExpression bridgeExpressionNew() {
835 if (tree.kind == ReferenceKind.ARRAY_CTOR) {
836 //create the array creation expression
837 JCNewArray newArr = make.NewArray(
838 make.Type(types.elemtype(tree.getQualifierExpression().type)),
839 List.of(make.Ident(params.first())),
840 null);
841 newArr.type = tree.getQualifierExpression().type;
842 return newArr;
843 } else {
844 JCExpression encl = null;
845 switch (tree.kind) {
846 case UNBOUND:
847 case IMPLICIT_INNER:
848 encl = make.Ident(params.first());
849 }
851 //create the instance creation expression
852 JCNewClass newClass = make.NewClass(encl,
853 List.<JCExpression>nil(),
854 make.Type(tree.getQualifierExpression().type),
855 convertArgs(tree.sym, args.toList(), tree.varargsElement),
856 null);
857 newClass.constructor = tree.sym;
858 newClass.constructorType = tree.sym.erasure(types);
859 newClass.type = tree.getQualifierExpression().type;
860 setVarargsIfNeeded(newClass, tree.varargsElement);
861 return newClass;
862 }
863 }
865 private VarSymbol addParameter(String name, Type p, boolean genArg) {
866 VarSymbol vsym = new VarSymbol(0, names.fromString(name), p, localContext.bridgeSym);
867 params.append(make.VarDef(vsym, null));
868 if (genArg) {
869 args.append(make.Ident(vsym));
870 }
871 return vsym;
872 }
873 }
875 /**
876 * Bridges a member reference - this is needed when:
877 * * Var args in the referenced method need to be flattened away
878 * * super is used
879 */
880 private void bridgeMemberReference(JCMemberReference tree, ReferenceTranslationContext localContext) {
881 kInfo.addMethod(new MemberReferenceBridger(tree, localContext).bridge());
882 }
884 /**
885 * Generate an indy method call to the meta factory
886 */
887 private JCExpression makeMetaFactoryIndyCall(JCFunctionalExpression tree, boolean needsAltMetafactory,
888 boolean isSerializable, int refKind, Symbol refSym, List<JCExpression> indy_args) {
889 //determine the static bsm args
890 Type mtype = types.erasure(tree.descriptorType);
891 MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
892 List<Object> staticArgs = List.<Object>of(
893 new Pool.MethodHandle(ClassFile.REF_invokeInterface,
894 types.findDescriptorSymbol(tree.type.tsym), types),
895 new Pool.MethodHandle(refKind, refSym, types),
896 new MethodType(mtype.getParameterTypes(),
897 mtype.getReturnType(),
898 mtype.getThrownTypes(),
899 syms.methodClass));
901 //computed indy arg types
902 ListBuffer<Type> indy_args_types = ListBuffer.lb();
903 for (JCExpression arg : indy_args) {
904 indy_args_types.append(arg.type);
905 }
907 //finally, compute the type of the indy call
908 MethodType indyType = new MethodType(indy_args_types.toList(),
909 tree.type,
910 List.<Type>nil(),
911 syms.methodClass);
913 Name metafactoryName = needsAltMetafactory ?
914 names.altMetaFactory : names.metaFactory;
916 if (needsAltMetafactory) {
917 ListBuffer<Object> markers = ListBuffer.lb();
918 for (Symbol t : tree.targets.tail) {
919 if (t != syms.serializableType.tsym) {
920 markers.append(t);
921 }
922 }
923 int flags = isSerializable? FLAG_SERIALIZABLE : 0;
924 boolean hasMarkers = markers.nonEmpty();
925 flags |= hasMarkers ? FLAG_MARKERS : 0;
926 staticArgs = staticArgs.append(flags);
927 if (hasMarkers) {
928 staticArgs = staticArgs.append(markers.length());
929 staticArgs = staticArgs.appendList(markers.toList());
930 }
931 if (isSerializable) {
932 addDeserializationCase(refKind, refSym, tree.type, samSym,
933 tree, staticArgs, indyType);
934 }
935 }
937 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args);
938 }
940 /**
941 * Generate an indy method call with given name, type and static bootstrap
942 * arguments types
943 */
944 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
945 List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs) {
946 int prevPos = make.pos;
947 try {
948 make.at(pos);
949 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
950 syms.stringType,
951 syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs));
953 Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site,
954 bsmName, bsm_staticArgs, List.<Type>nil());
956 DynamicMethodSymbol dynSym =
957 new DynamicMethodSymbol(names.lambda,
958 syms.noSymbol,
959 bsm.isStatic() ?
960 ClassFile.REF_invokeStatic :
961 ClassFile.REF_invokeVirtual,
962 (MethodSymbol)bsm,
963 indyType,
964 staticArgs.toArray());
966 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName);
967 qualifier.sym = dynSym;
968 qualifier.type = indyType.getReturnType();
970 JCMethodInvocation proxyCall = make.Apply(List.<JCExpression>nil(), qualifier, indyArgs);
971 proxyCall.type = indyType.getReturnType();
972 return proxyCall;
973 } finally {
974 make.at(prevPos);
975 }
976 }
977 //where
978 private List<Type> bsmStaticArgToTypes(List<Object> args) {
979 ListBuffer<Type> argtypes = ListBuffer.lb();
980 for (Object arg : args) {
981 argtypes.append(bsmStaticArgToType(arg));
982 }
983 return argtypes.toList();
984 }
986 private Type bsmStaticArgToType(Object arg) {
987 Assert.checkNonNull(arg);
988 if (arg instanceof ClassSymbol) {
989 return syms.classType;
990 } else if (arg instanceof Integer) {
991 return syms.intType;
992 } else if (arg instanceof Long) {
993 return syms.longType;
994 } else if (arg instanceof Float) {
995 return syms.floatType;
996 } else if (arg instanceof Double) {
997 return syms.doubleType;
998 } else if (arg instanceof String) {
999 return syms.stringType;
1000 } else if (arg instanceof Pool.MethodHandle) {
1001 return syms.methodHandleType;
1002 } else if (arg instanceof MethodType) {
1003 return syms.methodTypeType;
1004 } else {
1005 Assert.error("bad static arg " + arg.getClass());
1006 return null;
1007 }
1008 }
1010 /**
1011 * Get the opcode associated with this method reference
1012 */
1013 private int referenceKind(Symbol refSym) {
1014 if (refSym.isConstructor()) {
1015 return ClassFile.REF_newInvokeSpecial;
1016 } else {
1017 if (refSym.isStatic()) {
1018 return ClassFile.REF_invokeStatic;
1019 } else if (refSym.enclClass().isInterface()) {
1020 return ClassFile.REF_invokeInterface;
1021 } else {
1022 return ClassFile.REF_invokeVirtual;
1023 }
1024 }
1025 }
1027 // </editor-fold>
1029 // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer">\
1030 /**
1031 * This visitor collects information about translation of a lambda expression.
1032 * More specifically, it keeps track of the enclosing contexts and captured locals
1033 * accessed by the lambda being translated (as well as other useful info).
1034 */
1035 class LambdaAnalyzer extends TreeScanner {
1037 /** the frame stack - used to reconstruct translation info about enclosing scopes */
1038 private List<Frame> frameStack;
1040 /**
1041 * keep the count of lambda expression (used to generate unambiguous
1042 * names)
1043 */
1044 private int lambdaCount = 0;
1046 /**
1047 * keep the count of lambda expression defined in given context (used to
1048 * generate unambiguous names for serializable lambdas)
1049 */
1050 private Map<String, Integer> serializableLambdaCounts =
1051 new HashMap<String, Integer>();
1053 private Map<Symbol, JCClassDecl> localClassDefs;
1055 /**
1056 * maps for fake clinit symbols to be used as owners of lambda occurring in
1057 * a static var init context
1058 */
1059 private Map<ClassSymbol, Symbol> clinits =
1060 new HashMap<ClassSymbol, Symbol>();
1062 private void analyzeClass(JCClassDecl tree) {
1063 frameStack = List.nil();
1064 localClassDefs = new HashMap<Symbol, JCClassDecl>();
1065 scan(tree);
1066 }
1068 @Override
1069 public void visitBlock(JCBlock tree) {
1070 List<Frame> prevStack = frameStack;
1071 try {
1072 if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) {
1073 frameStack = frameStack.prepend(new Frame(tree));
1074 }
1075 super.visitBlock(tree);
1076 }
1077 finally {
1078 frameStack = prevStack;
1079 }
1080 }
1082 @Override
1083 public void visitClassDef(JCClassDecl tree) {
1084 List<Frame> prevStack = frameStack;
1085 Map<String, Integer> prevSerializableLambdaCount =
1086 serializableLambdaCounts;
1087 Map<ClassSymbol, Symbol> prevClinits = clinits;
1088 try {
1089 serializableLambdaCounts = new HashMap<String, Integer>();
1090 prevClinits = new HashMap<ClassSymbol, Symbol>();
1091 if (tree.sym.owner.kind == MTH) {
1092 localClassDefs.put(tree.sym, tree);
1093 }
1094 if (directlyEnclosingLambda() != null) {
1095 tree.sym.owner = owner();
1096 if (tree.sym.hasOuterInstance()) {
1097 //if a class is defined within a lambda, the lambda must capture
1098 //its enclosing instance (if any)
1099 TranslationContext<?> localContext = context();
1100 while (localContext != null) {
1101 if (localContext.tree.getTag() == LAMBDA) {
1102 ((LambdaTranslationContext)localContext)
1103 .addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS);
1104 }
1105 localContext = localContext.prev;
1106 }
1107 }
1108 }
1109 frameStack = frameStack.prepend(new Frame(tree));
1110 super.visitClassDef(tree);
1111 }
1112 finally {
1113 frameStack = prevStack;
1114 serializableLambdaCounts = prevSerializableLambdaCount;
1115 clinits = prevClinits;
1116 }
1117 }
1119 @Override
1120 public void visitIdent(JCIdent tree) {
1121 if (context() != null && lambdaIdentSymbolFilter(tree.sym)) {
1122 if (tree.sym.kind == VAR &&
1123 tree.sym.owner.kind == MTH &&
1124 tree.type.constValue() == null) {
1125 TranslationContext<?> localContext = context();
1126 while (localContext != null) {
1127 if (localContext.tree.getTag() == LAMBDA) {
1128 JCTree block = capturedDecl(localContext.depth, tree.sym);
1129 if (block == null) break;
1130 ((LambdaTranslationContext)localContext)
1131 .addSymbol(tree.sym, CAPTURED_VAR);
1132 }
1133 localContext = localContext.prev;
1134 }
1135 } else if (tree.sym.owner.kind == TYP) {
1136 TranslationContext<?> localContext = context();
1137 while (localContext != null) {
1138 if (localContext.tree.hasTag(LAMBDA)) {
1139 JCTree block = capturedDecl(localContext.depth, tree.sym);
1140 if (block == null) break;
1141 switch (block.getTag()) {
1142 case CLASSDEF:
1143 JCClassDecl cdecl = (JCClassDecl)block;
1144 ((LambdaTranslationContext)localContext)
1145 .addSymbol(cdecl.sym, CAPTURED_THIS);
1146 break;
1147 default:
1148 Assert.error("bad block kind");
1149 }
1150 }
1151 localContext = localContext.prev;
1152 }
1153 }
1154 }
1155 super.visitIdent(tree);
1156 }
1158 @Override
1159 public void visitLambda(JCLambda tree) {
1160 List<Frame> prevStack = frameStack;
1161 try {
1162 LambdaTranslationContext context = (LambdaTranslationContext)makeLambdaContext(tree);
1163 frameStack = frameStack.prepend(new Frame(tree));
1164 for (JCVariableDecl param : tree.params) {
1165 context.addSymbol(param.sym, PARAM);
1166 frameStack.head.addLocal(param.sym);
1167 }
1168 contextMap.put(tree, context);
1169 scan(tree.body);
1170 context.complete();
1171 }
1172 finally {
1173 frameStack = prevStack;
1174 }
1175 }
1177 @Override
1178 public void visitMethodDef(JCMethodDecl tree) {
1179 List<Frame> prevStack = frameStack;
1180 try {
1181 frameStack = frameStack.prepend(new Frame(tree));
1182 super.visitMethodDef(tree);
1183 }
1184 finally {
1185 frameStack = prevStack;
1186 }
1187 }
1189 @Override
1190 public void visitNewClass(JCNewClass tree) {
1191 if (lambdaNewClassFilter(context(), tree)) {
1192 TranslationContext<?> localContext = context();
1193 while (localContext != null) {
1194 if (localContext.tree.getTag() == LAMBDA) {
1195 ((LambdaTranslationContext)localContext)
1196 .addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS);
1197 }
1198 localContext = localContext.prev;
1199 }
1200 }
1201 if (context() != null && tree.type.tsym.owner.kind == MTH) {
1202 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context();
1203 captureLocalClassDefs(tree.type.tsym, lambdaContext);
1204 }
1205 super.visitNewClass(tree);
1206 }
1207 //where
1208 void captureLocalClassDefs(Symbol csym, final LambdaTranslationContext lambdaContext) {
1209 JCClassDecl localCDef = localClassDefs.get(csym);
1210 if (localCDef != null && localCDef.pos < lambdaContext.tree.pos) {
1211 BasicFreeVarCollector fvc = lower.new BasicFreeVarCollector() {
1212 @Override
1213 void addFreeVars(ClassSymbol c) {
1214 captureLocalClassDefs(c, lambdaContext);
1215 }
1216 @Override
1217 void visitSymbol(Symbol sym) {
1218 if (sym.kind == VAR &&
1219 sym.owner.kind == MTH &&
1220 ((VarSymbol)sym).getConstValue() == null) {
1221 TranslationContext<?> localContext = context();
1222 while (localContext != null) {
1223 if (localContext.tree.getTag() == LAMBDA) {
1224 JCTree block = capturedDecl(localContext.depth, sym);
1225 if (block == null) break;
1226 ((LambdaTranslationContext)localContext).addSymbol(sym, CAPTURED_VAR);
1227 }
1228 localContext = localContext.prev;
1229 }
1230 }
1231 }
1232 };
1233 fvc.scan(localCDef);
1234 }
1235 }
1237 @Override
1238 public void visitReference(JCMemberReference tree) {
1239 scan(tree.getQualifierExpression());
1240 contextMap.put(tree, makeReferenceContext(tree));
1241 }
1243 @Override
1244 public void visitSelect(JCFieldAccess tree) {
1245 if (context() != null && lambdaSelectSymbolFilter(tree.sym)) {
1246 TranslationContext<?> localContext = context();
1247 while (localContext != null) {
1248 if (localContext.tree.hasTag(LAMBDA)) {
1249 JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym);
1250 if (clazz == null) break;
1251 ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS);
1252 }
1253 localContext = localContext.prev;
1254 }
1255 scan(tree.selected);
1256 } else {
1257 super.visitSelect(tree);
1258 }
1259 }
1261 @Override
1262 public void visitVarDef(JCVariableDecl tree) {
1263 TranslationContext<?> context = context();
1264 LambdaTranslationContext ltc = (context != null && context instanceof LambdaTranslationContext)?
1265 (LambdaTranslationContext)context :
1266 null;
1267 if (ltc != null) {
1268 if (frameStack.head.tree.hasTag(LAMBDA)) {
1269 ltc.addSymbol(tree.sym, LOCAL_VAR);
1270 }
1271 // Check for type variables (including as type arguments).
1272 // If they occur within class nested in a lambda, mark for erasure
1273 Type type = tree.sym.asType();
1274 if (inClassWithinLambda() && !types.isSameType(types.erasure(type), type)) {
1275 ltc.addSymbol(tree.sym, TYPE_VAR);
1276 }
1277 }
1279 List<Frame> prevStack = frameStack;
1280 try {
1281 if (tree.sym.owner.kind == MTH) {
1282 frameStack.head.addLocal(tree.sym);
1283 }
1284 frameStack = frameStack.prepend(new Frame(tree));
1285 super.visitVarDef(tree);
1286 }
1287 finally {
1288 frameStack = prevStack;
1289 }
1290 }
1292 private Name lambdaName() {
1293 return names.lambda.append(names.fromString("" + lambdaCount++));
1294 }
1296 private Name serializedLambdaName(Symbol owner) {
1297 StringBuilder buf = new StringBuilder();
1298 buf.append(names.lambda);
1299 String methodName = owner.name.toString();
1300 if (methodName.equals("<clinit>"))
1301 methodName = "static";
1302 else if (methodName.equals("<init>"))
1303 methodName = "new";
1304 buf.append(methodName);
1305 buf.append('$');
1306 int methTypeHash = methodSig(owner.type).hashCode();
1307 buf.append(Integer.toHexString(methTypeHash));
1308 buf.append('$');
1309 String temp = buf.toString();
1310 Integer count = serializableLambdaCounts.get(temp);
1311 if (count == null) {
1312 count = 0;
1313 }
1314 buf.append(count++);
1315 serializableLambdaCounts.put(temp, count);
1316 return names.fromString(buf.toString());
1317 }
1319 /**
1320 * Return a valid owner given the current declaration stack
1321 * (required to skip synthetic lambda symbols)
1322 */
1323 private Symbol owner() {
1324 return owner(false);
1325 }
1327 @SuppressWarnings("fallthrough")
1328 private Symbol owner(boolean skipLambda) {
1329 List<Frame> frameStack2 = frameStack;
1330 while (frameStack2.nonEmpty()) {
1331 switch (frameStack2.head.tree.getTag()) {
1332 case VARDEF:
1333 if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) {
1334 frameStack2 = frameStack2.tail;
1335 break;
1336 }
1337 JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree;
1338 return initSym(cdecl.sym,
1339 ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC);
1340 case BLOCK:
1341 JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree;
1342 return initSym(cdecl2.sym,
1343 ((JCBlock)frameStack2.head.tree).flags & STATIC);
1344 case CLASSDEF:
1345 return ((JCClassDecl)frameStack2.head.tree).sym;
1346 case METHODDEF:
1347 return ((JCMethodDecl)frameStack2.head.tree).sym;
1348 case LAMBDA:
1349 if (!skipLambda)
1350 return ((LambdaTranslationContext)contextMap
1351 .get(frameStack2.head.tree)).translatedSym;
1352 default:
1353 frameStack2 = frameStack2.tail;
1354 }
1355 }
1356 Assert.error();
1357 return null;
1358 }
1360 private Symbol initSym(ClassSymbol csym, long flags) {
1361 boolean isStatic = (flags & STATIC) != 0;
1362 if (isStatic) {
1363 //static clinits are generated in Gen - so we need to fake them
1364 Symbol clinit = clinits.get(csym);
1365 if (clinit == null) {
1366 clinit = makeSyntheticMethod(STATIC,
1367 names.clinit,
1368 new MethodType(List.<Type>nil(), syms.voidType, List.<Type>nil(), syms.methodClass),
1369 csym);
1370 clinits.put(csym, clinit);
1371 }
1372 return clinit;
1373 } else {
1374 //get the first constructor and treat it as the instance init sym
1375 for (Symbol s : csym.members_field.getElementsByName(names.init)) {
1376 return s;
1377 }
1378 }
1379 Assert.error("init not found");
1380 return null;
1381 }
1383 private JCTree directlyEnclosingLambda() {
1384 if (frameStack.isEmpty()) {
1385 return null;
1386 }
1387 List<Frame> frameStack2 = frameStack;
1388 while (frameStack2.nonEmpty()) {
1389 switch (frameStack2.head.tree.getTag()) {
1390 case CLASSDEF:
1391 case METHODDEF:
1392 return null;
1393 case LAMBDA:
1394 return frameStack2.head.tree;
1395 default:
1396 frameStack2 = frameStack2.tail;
1397 }
1398 }
1399 Assert.error();
1400 return null;
1401 }
1403 private boolean inClassWithinLambda() {
1404 if (frameStack.isEmpty()) {
1405 return false;
1406 }
1407 List<Frame> frameStack2 = frameStack;
1408 boolean classFound = false;
1409 while (frameStack2.nonEmpty()) {
1410 switch (frameStack2.head.tree.getTag()) {
1411 case LAMBDA:
1412 return classFound;
1413 case CLASSDEF:
1414 classFound = true;
1415 frameStack2 = frameStack2.tail;
1416 break;
1417 default:
1418 frameStack2 = frameStack2.tail;
1419 }
1420 }
1421 // No lambda
1422 return false;
1423 }
1425 /**
1426 * Return the declaration corresponding to a symbol in the enclosing
1427 * scope; the depth parameter is used to filter out symbols defined
1428 * in nested scopes (which do not need to undergo capture).
1429 */
1430 private JCTree capturedDecl(int depth, Symbol sym) {
1431 int currentDepth = frameStack.size() - 1;
1432 for (Frame block : frameStack) {
1433 switch (block.tree.getTag()) {
1434 case CLASSDEF:
1435 ClassSymbol clazz = ((JCClassDecl)block.tree).sym;
1436 if (sym.isMemberOf(clazz, types)) {
1437 return currentDepth > depth ? null : block.tree;
1438 }
1439 break;
1440 case VARDEF:
1441 if (((JCVariableDecl)block.tree).sym == sym &&
1442 sym.owner.kind == MTH) { //only locals are captured
1443 return currentDepth > depth ? null : block.tree;
1444 }
1445 break;
1446 case BLOCK:
1447 case METHODDEF:
1448 case LAMBDA:
1449 if (block.locals != null && block.locals.contains(sym)) {
1450 return currentDepth > depth ? null : block.tree;
1451 }
1452 break;
1453 default:
1454 Assert.error("bad decl kind " + block.tree.getTag());
1455 }
1456 currentDepth--;
1457 }
1458 return null;
1459 }
1461 private TranslationContext<?> context() {
1462 for (Frame frame : frameStack) {
1463 TranslationContext<?> context = contextMap.get(frame.tree);
1464 if (context != null) {
1465 return context;
1466 }
1467 }
1468 return null;
1469 }
1471 /**
1472 * This is used to filter out those identifiers that needs to be adjusted
1473 * when translating away lambda expressions
1474 */
1475 private boolean lambdaIdentSymbolFilter(Symbol sym) {
1476 return (sym.kind == VAR || sym.kind == MTH)
1477 && !sym.isStatic()
1478 && sym.name != names.init;
1479 }
1481 private boolean lambdaSelectSymbolFilter(Symbol sym) {
1482 return (sym.kind == VAR || sym.kind == MTH) &&
1483 !sym.isStatic() &&
1484 (sym.name == names._this ||
1485 sym.name == names._super);
1486 }
1488 /**
1489 * This is used to filter out those new class expressions that need to
1490 * be qualified with an enclosing tree
1491 */
1492 private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) {
1493 if (context != null
1494 && tree.encl == null
1495 && tree.def == null
1496 && !tree.type.getEnclosingType().hasTag(NONE)) {
1497 Type encl = tree.type.getEnclosingType();
1498 Type current = context.owner.enclClass().type;
1499 while (!current.hasTag(NONE)) {
1500 if (current.tsym.isSubClass(encl.tsym, types)) {
1501 return true;
1502 }
1503 current = current.getEnclosingType();
1504 }
1505 return false;
1506 } else {
1507 return false;
1508 }
1509 }
1511 private TranslationContext<JCLambda> makeLambdaContext(JCLambda tree) {
1512 return new LambdaTranslationContext(tree);
1513 }
1515 private TranslationContext<JCMemberReference> makeReferenceContext(JCMemberReference tree) {
1516 return new ReferenceTranslationContext(tree);
1517 }
1519 private class Frame {
1520 final JCTree tree;
1521 List<Symbol> locals;
1523 public Frame(JCTree tree) {
1524 this.tree = tree;
1525 }
1527 void addLocal(Symbol sym) {
1528 if (locals == null) {
1529 locals = List.nil();
1530 }
1531 locals = locals.prepend(sym);
1532 }
1533 }
1535 /**
1536 * This class is used to store important information regarding translation of
1537 * lambda expression/method references (see subclasses).
1538 */
1539 private abstract class TranslationContext<T extends JCFunctionalExpression> {
1541 /** the underlying (untranslated) tree */
1542 T tree;
1544 /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */
1545 Symbol owner;
1547 /** the depth of this lambda expression in the frame stack */
1548 int depth;
1550 /** the enclosing translation context (set for nested lambdas/mref) */
1551 TranslationContext<?> prev;
1553 TranslationContext(T tree) {
1554 this.tree = tree;
1555 this.owner = owner();
1556 this.depth = frameStack.size() - 1;
1557 this.prev = context();
1558 }
1560 /** does this functional expression need to be created using alternate metafactory? */
1561 boolean needsAltMetafactory() {
1562 return (tree.targets.length() > 1 ||
1563 isSerializable());
1564 }
1566 /** does this functional expression require serialization support? */
1567 boolean isSerializable() {
1568 for (Symbol target : tree.targets) {
1569 if (types.asSuper(target.type, syms.serializableType.tsym) != null) {
1570 return true;
1571 }
1572 }
1573 return false;
1574 }
1575 }
1577 /**
1578 * This class retains all the useful information about a lambda expression;
1579 * the contents of this class are filled by the LambdaAnalyzer visitor,
1580 * and the used by the main translation routines in order to adjust references
1581 * to captured locals/members, etc.
1582 */
1583 private class LambdaTranslationContext extends TranslationContext<JCLambda> {
1585 /** variable in the enclosing context to which this lambda is assigned */
1586 Symbol self;
1588 /** map from original to translated lambda parameters */
1589 Map<Symbol, Symbol> lambdaParams = new LinkedHashMap<Symbol, Symbol>();
1591 /** map from original to translated lambda locals */
1592 Map<Symbol, Symbol> lambdaLocals = new LinkedHashMap<Symbol, Symbol>();
1594 /** map from variables in enclosing scope to translated synthetic parameters */
1595 Map<Symbol, Symbol> capturedLocals = new LinkedHashMap<Symbol, Symbol>();
1597 /** map from class symbols to translated synthetic parameters (for captured member access) */
1598 Map<Symbol, Symbol> capturedThis = new LinkedHashMap<Symbol, Symbol>();
1600 /** map from original to translated lambda locals */
1601 Map<Symbol, Symbol> typeVars = new LinkedHashMap<Symbol, Symbol>();
1603 /** the synthetic symbol for the method hoisting the translated lambda */
1604 Symbol translatedSym;
1606 List<JCVariableDecl> syntheticParams;
1608 LambdaTranslationContext(JCLambda tree) {
1609 super(tree);
1610 Frame frame = frameStack.head;
1611 if (frame.tree.hasTag(VARDEF)) {
1612 self = ((JCVariableDecl)frame.tree).sym;
1613 }
1614 Name name = isSerializable() ? serializedLambdaName(owner) : lambdaName();
1615 this.translatedSym = makeSyntheticMethod(0, name, null, owner.enclClass());
1616 }
1618 /**
1619 * Translate a symbol of a given kind into something suitable for the
1620 * synthetic lambda body
1621 */
1622 Symbol translate(String name, final Symbol sym, LambdaSymbolKind skind) {
1623 switch (skind) {
1624 case CAPTURED_THIS:
1625 return sym; // self represented
1626 case TYPE_VAR:
1627 // Just erase the type var
1628 return new VarSymbol(sym.flags(), names.fromString(name),
1629 types.erasure(sym.type), sym.owner);
1630 case CAPTURED_VAR:
1631 return new VarSymbol(SYNTHETIC | FINAL, names.fromString(name), types.erasure(sym.type), translatedSym) {
1632 @Override
1633 public Symbol baseSymbol() {
1634 //keep mapping with original captured symbol
1635 return sym;
1636 }
1637 };
1638 default:
1639 return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
1640 }
1641 }
1643 void addSymbol(Symbol sym, LambdaSymbolKind skind) {
1644 Map<Symbol, Symbol> transMap = null;
1645 String preferredName;
1646 switch (skind) {
1647 case CAPTURED_THIS:
1648 transMap = capturedThis;
1649 preferredName = "encl$" + capturedThis.size();
1650 break;
1651 case CAPTURED_VAR:
1652 transMap = capturedLocals;
1653 preferredName = "cap$" + capturedLocals.size();
1654 break;
1655 case LOCAL_VAR:
1656 transMap = lambdaLocals;
1657 preferredName = sym.name.toString();
1658 break;
1659 case PARAM:
1660 transMap = lambdaParams;
1661 preferredName = sym.name.toString();
1662 break;
1663 case TYPE_VAR:
1664 transMap = typeVars;
1665 preferredName = sym.name.toString();
1666 break;
1667 default: throw new AssertionError();
1668 }
1669 if (!transMap.containsKey(sym)) {
1670 transMap.put(sym, translate(preferredName, sym, skind));
1671 }
1672 }
1674 Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind... skinds) {
1675 LinkedHashMap<Symbol, Symbol> translationMap = new LinkedHashMap<Symbol, Symbol>();
1676 for (LambdaSymbolKind skind : skinds) {
1677 switch (skind) {
1678 case CAPTURED_THIS:
1679 translationMap.putAll(capturedThis);
1680 break;
1681 case CAPTURED_VAR:
1682 translationMap.putAll(capturedLocals);
1683 break;
1684 case LOCAL_VAR:
1685 translationMap.putAll(lambdaLocals);
1686 break;
1687 case PARAM:
1688 translationMap.putAll(lambdaParams);
1689 break;
1690 case TYPE_VAR:
1691 translationMap.putAll(typeVars);
1692 break;
1693 default: throw new AssertionError();
1694 }
1695 }
1696 return translationMap;
1697 }
1699 /**
1700 * The translatedSym is not complete/accurate until the analysis is
1701 * finished. Once the analysis is finished, the translatedSym is
1702 * "completed" -- updated with type information, access modifiers,
1703 * and full parameter list.
1704 */
1705 void complete() {
1706 if (syntheticParams != null) {
1707 return;
1708 }
1709 boolean inInterface = translatedSym.owner.isInterface();
1710 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty();
1711 boolean needInstance = thisReferenced || inInterface;
1713 // If instance access isn't needed, make it static
1714 // Interface methods much be public default methods, otherwise make it private
1715 translatedSym.flags_field = SYNTHETIC | (needInstance? 0 : STATIC) |
1716 (inInterface? PUBLIC | DEFAULT : PRIVATE);
1718 //compute synthetic params
1719 ListBuffer<JCVariableDecl> params = ListBuffer.lb();
1721 // The signature of the method is augmented with the following
1722 // synthetic parameters:
1723 //
1724 // 1) reference to enclosing contexts captured by the lambda expression
1725 // 2) enclosing locals captured by the lambda expression
1726 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR, PARAM).values()) {
1727 params.append(make.VarDef((VarSymbol) thisSym, null));
1728 }
1730 syntheticParams = params.toList();
1732 //prepend synthetic args to translated lambda method signature
1733 translatedSym.type = types.createMethodTypeWithParameters(
1734 generatedLambdaSig(),
1735 TreeInfo.types(syntheticParams));
1736 }
1738 Type generatedLambdaSig() {
1739 return types.erasure(tree.descriptorType);
1740 }
1741 }
1743 /**
1744 * This class retains all the useful information about a method reference;
1745 * the contents of this class are filled by the LambdaAnalyzer visitor,
1746 * and the used by the main translation routines in order to adjust method
1747 * references (i.e. in case a bridge is needed)
1748 */
1749 private class ReferenceTranslationContext extends TranslationContext<JCMemberReference> {
1751 final boolean isSuper;
1752 final Symbol bridgeSym;
1754 ReferenceTranslationContext(JCMemberReference tree) {
1755 super(tree);
1756 this.isSuper = tree.hasKind(ReferenceKind.SUPER);
1757 this.bridgeSym = needsBridge()
1758 ? makeSyntheticMethod(isSuper ? 0 : STATIC,
1759 lambdaName().append(names.fromString("$bridge")), null,
1760 owner.enclClass())
1761 : null;
1762 }
1764 /**
1765 * Get the opcode associated with this method reference
1766 */
1767 int referenceKind() {
1768 return LambdaToMethod.this.referenceKind(needsBridge() ? bridgeSym : tree.sym);
1769 }
1771 boolean needsVarArgsConversion() {
1772 return tree.varargsElement != null;
1773 }
1775 /**
1776 * @return Is this an array operation like clone()
1777 */
1778 boolean isArrayOp() {
1779 return tree.sym.owner == syms.arrayClass;
1780 }
1782 boolean isPrivateConstructor() {
1783 //hack needed to workaround 292 bug (8005122)
1784 //when 292 issue is fixed we should simply remove this
1785 return tree.sym.name == names.init &&
1786 (tree.sym.flags() & PRIVATE) != 0;
1787 }
1789 boolean receiverAccessible() {
1790 //hack needed to workaround 292 bug (7087658)
1791 //when 292 issue is fixed we should remove this and change the backend
1792 //code to always generate a method handle to an accessible method
1793 return tree.ownerAccessible;
1794 }
1796 /**
1797 * Does this reference needs a bridge (i.e. var args need to be
1798 * expanded or "super" is used)
1799 */
1800 final boolean needsBridge() {
1801 return isSuper || needsVarArgsConversion() || isArrayOp() ||
1802 isPrivateConstructor() || !receiverAccessible();
1803 }
1805 Type generatedRefSig() {
1806 return types.erasure(tree.sym.type);
1807 }
1809 Type bridgedRefSig() {
1810 return types.erasure(types.findDescriptorSymbol(tree.targets.head).type);
1811 }
1812 }
1813 }
1814 // </editor-fold>
1816 enum LambdaSymbolKind {
1817 CAPTURED_VAR,
1818 CAPTURED_THIS,
1819 LOCAL_VAR,
1820 PARAM,
1821 TYPE_VAR;
1822 }
1824 /**
1825 * ****************************************************************
1826 * Signature Generation
1827 * ****************************************************************
1828 */
1830 private String methodSig(Type type) {
1831 L2MSignatureGenerator sg = new L2MSignatureGenerator();
1832 sg.assembleSig(type);
1833 return sg.toString();
1834 }
1836 private String classSig(Type type) {
1837 L2MSignatureGenerator sg = new L2MSignatureGenerator();
1838 sg.assembleClassSig(type);
1839 return sg.toString();
1840 }
1842 /**
1843 * Signature Generation
1844 */
1845 private class L2MSignatureGenerator extends Types.SignatureGenerator {
1847 /**
1848 * An output buffer for type signatures.
1849 */
1850 StringBuilder sb = new StringBuilder();
1852 L2MSignatureGenerator() {
1853 super(types);
1854 }
1856 @Override
1857 protected void append(char ch) {
1858 sb.append(ch);
1859 }
1861 @Override
1862 protected void append(byte[] ba) {
1863 sb.append(new String(ba));
1864 }
1866 @Override
1867 protected void append(Name name) {
1868 sb.append(name.toString());
1869 }
1871 @Override
1872 public String toString() {
1873 return sb.toString();
1874 }
1875 }
1876 }