Tue, 05 Mar 2013 14:19:49 +0000
8009129: Illegal access error when calling method reference
Summary: Javac generates method handle referencing non public type
Reviewed-by: jjg, rfield
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(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 buf.append(owner.name);
1300 buf.append('$');
1301 int methTypeHash = methodSig(owner.type).hashCode();
1302 buf.append(methTypeHash);
1303 buf.append('$');
1304 String temp = buf.toString();
1305 Integer count = serializableLambdaCounts.get(temp);
1306 if (count == null) {
1307 count = 0;
1308 }
1309 buf.append(count++);
1310 serializableLambdaCounts.put(temp, count);
1311 return names.fromString(buf.toString());
1312 }
1314 /**
1315 * Return a valid owner given the current declaration stack
1316 * (required to skip synthetic lambda symbols)
1317 */
1318 private Symbol owner() {
1319 return owner(false);
1320 }
1322 @SuppressWarnings("fallthrough")
1323 private Symbol owner(boolean skipLambda) {
1324 List<Frame> frameStack2 = frameStack;
1325 while (frameStack2.nonEmpty()) {
1326 switch (frameStack2.head.tree.getTag()) {
1327 case VARDEF:
1328 if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) {
1329 frameStack2 = frameStack2.tail;
1330 break;
1331 }
1332 JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree;
1333 return initSym(cdecl.sym,
1334 ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC);
1335 case BLOCK:
1336 JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree;
1337 return initSym(cdecl2.sym,
1338 ((JCBlock)frameStack2.head.tree).flags & STATIC);
1339 case CLASSDEF:
1340 return ((JCClassDecl)frameStack2.head.tree).sym;
1341 case METHODDEF:
1342 return ((JCMethodDecl)frameStack2.head.tree).sym;
1343 case LAMBDA:
1344 if (!skipLambda)
1345 return ((LambdaTranslationContext)contextMap
1346 .get(frameStack2.head.tree)).translatedSym;
1347 default:
1348 frameStack2 = frameStack2.tail;
1349 }
1350 }
1351 Assert.error();
1352 return null;
1353 }
1355 private Symbol initSym(ClassSymbol csym, long flags) {
1356 boolean isStatic = (flags & STATIC) != 0;
1357 if (isStatic) {
1358 //static clinits are generated in Gen - so we need to fake them
1359 Symbol clinit = clinits.get(csym);
1360 if (clinit == null) {
1361 clinit = makeSyntheticMethod(STATIC,
1362 names.clinit,
1363 new MethodType(List.<Type>nil(), syms.voidType, List.<Type>nil(), syms.methodClass),
1364 csym);
1365 clinits.put(csym, clinit);
1366 }
1367 return clinit;
1368 } else {
1369 //get the first constructor and treat it as the instance init sym
1370 for (Symbol s : csym.members_field.getElementsByName(names.init)) {
1371 return s;
1372 }
1373 }
1374 Assert.error("init not found");
1375 return null;
1376 }
1378 private JCTree directlyEnclosingLambda() {
1379 if (frameStack.isEmpty()) {
1380 return null;
1381 }
1382 List<Frame> frameStack2 = frameStack;
1383 while (frameStack2.nonEmpty()) {
1384 switch (frameStack2.head.tree.getTag()) {
1385 case CLASSDEF:
1386 case METHODDEF:
1387 return null;
1388 case LAMBDA:
1389 return frameStack2.head.tree;
1390 default:
1391 frameStack2 = frameStack2.tail;
1392 }
1393 }
1394 Assert.error();
1395 return null;
1396 }
1398 private boolean inClassWithinLambda() {
1399 if (frameStack.isEmpty()) {
1400 return false;
1401 }
1402 List<Frame> frameStack2 = frameStack;
1403 boolean classFound = false;
1404 while (frameStack2.nonEmpty()) {
1405 switch (frameStack2.head.tree.getTag()) {
1406 case LAMBDA:
1407 return classFound;
1408 case CLASSDEF:
1409 classFound = true;
1410 frameStack2 = frameStack2.tail;
1411 break;
1412 default:
1413 frameStack2 = frameStack2.tail;
1414 }
1415 }
1416 // No lambda
1417 return false;
1418 }
1420 /**
1421 * Return the declaration corresponding to a symbol in the enclosing
1422 * scope; the depth parameter is used to filter out symbols defined
1423 * in nested scopes (which do not need to undergo capture).
1424 */
1425 private JCTree capturedDecl(int depth, Symbol sym) {
1426 int currentDepth = frameStack.size() - 1;
1427 for (Frame block : frameStack) {
1428 switch (block.tree.getTag()) {
1429 case CLASSDEF:
1430 ClassSymbol clazz = ((JCClassDecl)block.tree).sym;
1431 if (sym.isMemberOf(clazz, types)) {
1432 return currentDepth > depth ? null : block.tree;
1433 }
1434 break;
1435 case VARDEF:
1436 if (((JCVariableDecl)block.tree).sym == sym &&
1437 sym.owner.kind == MTH) { //only locals are captured
1438 return currentDepth > depth ? null : block.tree;
1439 }
1440 break;
1441 case BLOCK:
1442 case METHODDEF:
1443 case LAMBDA:
1444 if (block.locals != null && block.locals.contains(sym)) {
1445 return currentDepth > depth ? null : block.tree;
1446 }
1447 break;
1448 default:
1449 Assert.error("bad decl kind " + block.tree.getTag());
1450 }
1451 currentDepth--;
1452 }
1453 return null;
1454 }
1456 private TranslationContext<?> context() {
1457 for (Frame frame : frameStack) {
1458 TranslationContext<?> context = contextMap.get(frame.tree);
1459 if (context != null) {
1460 return context;
1461 }
1462 }
1463 return null;
1464 }
1466 /**
1467 * This is used to filter out those identifiers that needs to be adjusted
1468 * when translating away lambda expressions
1469 */
1470 private boolean lambdaIdentSymbolFilter(Symbol sym) {
1471 return (sym.kind == VAR || sym.kind == MTH)
1472 && !sym.isStatic()
1473 && sym.name != names.init;
1474 }
1476 private boolean lambdaSelectSymbolFilter(Symbol sym) {
1477 return (sym.kind == VAR || sym.kind == MTH) &&
1478 !sym.isStatic() &&
1479 (sym.name == names._this ||
1480 sym.name == names._super);
1481 }
1483 /**
1484 * This is used to filter out those new class expressions that need to
1485 * be qualified with an enclosing tree
1486 */
1487 private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) {
1488 if (context != null
1489 && tree.encl == null
1490 && tree.def == null
1491 && !tree.type.getEnclosingType().hasTag(NONE)) {
1492 Type encl = tree.type.getEnclosingType();
1493 Type current = context.owner.enclClass().type;
1494 while (!current.hasTag(NONE)) {
1495 if (current.tsym.isSubClass(encl.tsym, types)) {
1496 return true;
1497 }
1498 current = current.getEnclosingType();
1499 }
1500 return false;
1501 } else {
1502 return false;
1503 }
1504 }
1506 private TranslationContext<JCLambda> makeLambdaContext(JCLambda tree) {
1507 return new LambdaTranslationContext(tree);
1508 }
1510 private TranslationContext<JCMemberReference> makeReferenceContext(JCMemberReference tree) {
1511 return new ReferenceTranslationContext(tree);
1512 }
1514 private class Frame {
1515 final JCTree tree;
1516 List<Symbol> locals;
1518 public Frame(JCTree tree) {
1519 this.tree = tree;
1520 }
1522 void addLocal(Symbol sym) {
1523 if (locals == null) {
1524 locals = List.nil();
1525 }
1526 locals = locals.prepend(sym);
1527 }
1528 }
1530 /**
1531 * This class is used to store important information regarding translation of
1532 * lambda expression/method references (see subclasses).
1533 */
1534 private abstract class TranslationContext<T extends JCFunctionalExpression> {
1536 /** the underlying (untranslated) tree */
1537 T tree;
1539 /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */
1540 Symbol owner;
1542 /** the depth of this lambda expression in the frame stack */
1543 int depth;
1545 /** the enclosing translation context (set for nested lambdas/mref) */
1546 TranslationContext<?> prev;
1548 TranslationContext(T tree) {
1549 this.tree = tree;
1550 this.owner = owner();
1551 this.depth = frameStack.size() - 1;
1552 this.prev = context();
1553 }
1555 /** does this functional expression need to be created using alternate metafactory? */
1556 boolean needsAltMetafactory() {
1557 return (tree.targets.length() > 1 ||
1558 isSerializable());
1559 }
1561 /** does this functional expression require serialization support? */
1562 boolean isSerializable() {
1563 for (Symbol target : tree.targets) {
1564 if (types.asSuper(target.type, syms.serializableType.tsym) != null) {
1565 return true;
1566 }
1567 }
1568 return false;
1569 }
1570 }
1572 /**
1573 * This class retains all the useful information about a lambda expression;
1574 * the contents of this class are filled by the LambdaAnalyzer visitor,
1575 * and the used by the main translation routines in order to adjust references
1576 * to captured locals/members, etc.
1577 */
1578 private class LambdaTranslationContext extends TranslationContext<JCLambda> {
1580 /** variable in the enclosing context to which this lambda is assigned */
1581 Symbol self;
1583 /** map from original to translated lambda parameters */
1584 Map<Symbol, Symbol> lambdaParams = new LinkedHashMap<Symbol, Symbol>();
1586 /** map from original to translated lambda locals */
1587 Map<Symbol, Symbol> lambdaLocals = new LinkedHashMap<Symbol, Symbol>();
1589 /** map from variables in enclosing scope to translated synthetic parameters */
1590 Map<Symbol, Symbol> capturedLocals = new LinkedHashMap<Symbol, Symbol>();
1592 /** map from class symbols to translated synthetic parameters (for captured member access) */
1593 Map<Symbol, Symbol> capturedThis = new LinkedHashMap<Symbol, Symbol>();
1595 /** map from original to translated lambda locals */
1596 Map<Symbol, Symbol> typeVars = new LinkedHashMap<Symbol, Symbol>();
1598 /** the synthetic symbol for the method hoisting the translated lambda */
1599 Symbol translatedSym;
1601 List<JCVariableDecl> syntheticParams;
1603 LambdaTranslationContext(JCLambda tree) {
1604 super(tree);
1605 Frame frame = frameStack.head;
1606 if (frame.tree.hasTag(VARDEF)) {
1607 self = ((JCVariableDecl)frame.tree).sym;
1608 }
1609 Name name = isSerializable() ? serializedLambdaName(owner) : lambdaName();
1610 this.translatedSym = makeSyntheticMethod(0, name, null, owner.enclClass());
1611 }
1613 /**
1614 * Translate a symbol of a given kind into something suitable for the
1615 * synthetic lambda body
1616 */
1617 Symbol translate(String name, final Symbol sym, LambdaSymbolKind skind) {
1618 switch (skind) {
1619 case CAPTURED_THIS:
1620 return sym; // self represented
1621 case TYPE_VAR:
1622 // Just erase the type var
1623 return new VarSymbol(sym.flags(), names.fromString(name),
1624 types.erasure(sym.type), sym.owner);
1625 case CAPTURED_VAR:
1626 return new VarSymbol(SYNTHETIC | FINAL, names.fromString(name), types.erasure(sym.type), translatedSym) {
1627 @Override
1628 public Symbol baseSymbol() {
1629 //keep mapping with original captured symbol
1630 return sym;
1631 }
1632 };
1633 default:
1634 return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
1635 }
1636 }
1638 void addSymbol(Symbol sym, LambdaSymbolKind skind) {
1639 Map<Symbol, Symbol> transMap = null;
1640 String preferredName;
1641 switch (skind) {
1642 case CAPTURED_THIS:
1643 transMap = capturedThis;
1644 preferredName = "encl$" + capturedThis.size();
1645 break;
1646 case CAPTURED_VAR:
1647 transMap = capturedLocals;
1648 preferredName = "cap$" + capturedLocals.size();
1649 break;
1650 case LOCAL_VAR:
1651 transMap = lambdaLocals;
1652 preferredName = sym.name.toString();
1653 break;
1654 case PARAM:
1655 transMap = lambdaParams;
1656 preferredName = sym.name.toString();
1657 break;
1658 case TYPE_VAR:
1659 transMap = typeVars;
1660 preferredName = sym.name.toString();
1661 break;
1662 default: throw new AssertionError();
1663 }
1664 if (!transMap.containsKey(sym)) {
1665 transMap.put(sym, translate(preferredName, sym, skind));
1666 }
1667 }
1669 Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind... skinds) {
1670 LinkedHashMap<Symbol, Symbol> translationMap = new LinkedHashMap<Symbol, Symbol>();
1671 for (LambdaSymbolKind skind : skinds) {
1672 switch (skind) {
1673 case CAPTURED_THIS:
1674 translationMap.putAll(capturedThis);
1675 break;
1676 case CAPTURED_VAR:
1677 translationMap.putAll(capturedLocals);
1678 break;
1679 case LOCAL_VAR:
1680 translationMap.putAll(lambdaLocals);
1681 break;
1682 case PARAM:
1683 translationMap.putAll(lambdaParams);
1684 break;
1685 case TYPE_VAR:
1686 translationMap.putAll(typeVars);
1687 break;
1688 default: throw new AssertionError();
1689 }
1690 }
1691 return translationMap;
1692 }
1694 /**
1695 * The translatedSym is not complete/accurate until the analysis is
1696 * finished. Once the analysis is finished, the translatedSym is
1697 * "completed" -- updated with type information, access modifiers,
1698 * and full parameter list.
1699 */
1700 void complete() {
1701 if (syntheticParams != null) {
1702 return;
1703 }
1704 boolean inInterface = translatedSym.owner.isInterface();
1705 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty();
1706 boolean needInstance = thisReferenced || inInterface;
1708 // If instance access isn't needed, make it static
1709 // Interface methods much be public default methods, otherwise make it private
1710 translatedSym.flags_field = SYNTHETIC | (needInstance? 0 : STATIC) |
1711 (inInterface? PUBLIC | DEFAULT : PRIVATE);
1713 //compute synthetic params
1714 ListBuffer<JCVariableDecl> params = ListBuffer.lb();
1716 // The signature of the method is augmented with the following
1717 // synthetic parameters:
1718 //
1719 // 1) reference to enclosing contexts captured by the lambda expression
1720 // 2) enclosing locals captured by the lambda expression
1721 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR, PARAM).values()) {
1722 params.append(make.VarDef((VarSymbol) thisSym, null));
1723 }
1725 syntheticParams = params.toList();
1727 //prepend synthetic args to translated lambda method signature
1728 translatedSym.type = types.createMethodTypeWithParameters(
1729 generatedLambdaSig(),
1730 TreeInfo.types(syntheticParams));
1731 }
1733 Type generatedLambdaSig() {
1734 return types.erasure(tree.descriptorType);
1735 }
1736 }
1738 /**
1739 * This class retains all the useful information about a method reference;
1740 * the contents of this class are filled by the LambdaAnalyzer visitor,
1741 * and the used by the main translation routines in order to adjust method
1742 * references (i.e. in case a bridge is needed)
1743 */
1744 private class ReferenceTranslationContext extends TranslationContext<JCMemberReference> {
1746 final boolean isSuper;
1747 final Symbol bridgeSym;
1749 ReferenceTranslationContext(JCMemberReference tree) {
1750 super(tree);
1751 this.isSuper = tree.hasKind(ReferenceKind.SUPER);
1752 this.bridgeSym = needsBridge()
1753 ? makeSyntheticMethod(isSuper ? 0 : STATIC,
1754 lambdaName().append(names.fromString("$bridge")), null,
1755 owner.enclClass())
1756 : null;
1757 }
1759 /**
1760 * Get the opcode associated with this method reference
1761 */
1762 int referenceKind() {
1763 return LambdaToMethod.this.referenceKind(needsBridge() ? bridgeSym : tree.sym);
1764 }
1766 boolean needsVarArgsConversion() {
1767 return tree.varargsElement != null;
1768 }
1770 /**
1771 * @return Is this an array operation like clone()
1772 */
1773 boolean isArrayOp() {
1774 return tree.sym.owner == syms.arrayClass;
1775 }
1777 boolean isPrivateConstructor() {
1778 //hack needed to workaround 292 bug (8005122)
1779 //when 292 issue is fixed we should simply remove this
1780 return tree.sym.name == names.init &&
1781 (tree.sym.flags() & PRIVATE) != 0;
1782 }
1784 boolean receiverAccessible() {
1785 //hack needed to workaround 292 bug (7087658)
1786 //when 292 issue is fixed we should remove this and change the backend
1787 //code to always generate a method handle to an accessible method
1788 return tree.ownerAccessible;
1789 }
1791 /**
1792 * Does this reference needs a bridge (i.e. var args need to be
1793 * expanded or "super" is used)
1794 */
1795 final boolean needsBridge() {
1796 return isSuper || needsVarArgsConversion() || isArrayOp() ||
1797 isPrivateConstructor() || !receiverAccessible();
1798 }
1800 Type generatedRefSig() {
1801 return types.erasure(tree.sym.type);
1802 }
1804 Type bridgedRefSig() {
1805 return types.erasure(types.findDescriptorSymbol(tree.targets.head).type);
1806 }
1807 }
1808 }
1809 // </editor-fold>
1811 enum LambdaSymbolKind {
1812 CAPTURED_VAR,
1813 CAPTURED_THIS,
1814 LOCAL_VAR,
1815 PARAM,
1816 TYPE_VAR;
1817 }
1819 /**
1820 * ****************************************************************
1821 * Signature Generation
1822 * ****************************************************************
1823 */
1825 private String methodSig(Type type) {
1826 L2MSignatureGenerator sg = new L2MSignatureGenerator();
1827 sg.assembleSig(type);
1828 return sg.toString();
1829 }
1831 private String classSig(Type type) {
1832 L2MSignatureGenerator sg = new L2MSignatureGenerator();
1833 sg.assembleClassSig(type);
1834 return sg.toString();
1835 }
1837 /**
1838 * Signature Generation
1839 */
1840 private class L2MSignatureGenerator extends Types.SignatureGenerator {
1842 /**
1843 * An output buffer for type signatures.
1844 */
1845 StringBuilder sb = new StringBuilder();
1847 L2MSignatureGenerator() {
1848 super(types);
1849 }
1851 @Override
1852 protected void append(char ch) {
1853 sb.append(ch);
1854 }
1856 @Override
1857 protected void append(byte[] ba) {
1858 sb.append(new String(ba));
1859 }
1861 @Override
1862 protected void append(Name name) {
1863 sb.append(name.toString());
1864 }
1866 @Override
1867 public String toString() {
1868 return sb.toString();
1869 }
1870 }
1871 }