1 /*
2 * Copyright (c) 2010, 2022, 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
121 private final boolean debugLinesOrVars;
122
123 /** dump statistics about lambda method deduplication */
124 private final boolean verboseDeduplication;
125
126 /** deduplicate lambda implementation methods */
127 private final boolean deduplicateLambdas;
128
129 /** lambda proxy is a dynamic nestmate */
130 private final boolean nestmateLambdas;
131
132 /** Flag for alternate metafactories indicating the lambda object is intended to be serializable */
133 public static final int FLAG_SERIALIZABLE = 1 << 0;
134
135 /** Flag for alternate metafactories indicating the lambda object has multiple targets */
136 public static final int FLAG_MARKERS = 1 << 1;
137
138 /** Flag for alternate metafactories indicating the lambda object requires multiple bridges */
139 public static final int FLAG_BRIDGES = 1 << 2;
140
141 // <editor-fold defaultstate="collapsed" desc="Instantiating">
142 protected static final Context.Key<LambdaToMethod> unlambdaKey = new Context.Key<>();
143
144 public static LambdaToMethod instance(Context context) {
145 LambdaToMethod instance = context.get(unlambdaKey);
146 if (instance == null) {
147 instance = new LambdaToMethod(context);
148 }
149 return instance;
150 }
151 private LambdaToMethod(Context context) {
152 context.put(unlambdaKey, this);
153 diags = JCDiagnostic.Factory.instance(context);
154 log = Log.instance(context);
155 lower = Lower.instance(context);
156 names = Names.instance(context);
157 syms = Symtab.instance(context);
158 rs = Resolve.instance(context);
159 operators = Operators.instance(context);
160 make = TreeMaker.instance(context);
422 syntheticInits.append(localContext.methodReferenceReceiver);
423 } else if (!sym.isStatic()) {
424 syntheticInits.append(makeThis(
425 sym.owner.enclClass().asType(),
426 localContext.owner.enclClass()));
427 }
428
429 //add captured locals
430 for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) {
431 if (fv != localContext.self) {
432 JCExpression captured_local = make.Ident(fv).setType(fv.type);
433 syntheticInits.append(captured_local);
434 }
435 }
436 // add captured outer this instances (used only when `this' capture itself is illegal)
437 for (Symbol fv : localContext.getSymbolMap(CAPTURED_OUTER_THIS).keySet()) {
438 JCExpression captured_local = make.QualThis(fv.type);
439 syntheticInits.append(captured_local);
440 }
441
442 //then, determine the arguments to the indy call
443 List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev);
444
445 //convert to an invokedynamic call
446 result = makeMetafactoryIndyCall(context, sym.asHandle(), indy_args);
447 }
448
449 // where
450 // Reassign type annotations from the source that should really belong to the lambda
451 private void apportionTypeAnnotations(JCLambda tree,
452 Supplier<List<Attribute.TypeCompound>> source,
453 Consumer<List<Attribute.TypeCompound>> owner,
454 Consumer<List<Attribute.TypeCompound>> lambda) {
455
456 ListBuffer<Attribute.TypeCompound> ownerTypeAnnos = new ListBuffer<>();
457 ListBuffer<Attribute.TypeCompound> lambdaTypeAnnos = new ListBuffer<>();
458
459 for (Attribute.TypeCompound tc : source.get()) {
460 if (tc.position.onLambda == tree) {
461 lambdaTypeAnnos.append(tc);
503
504 case BOUND: /** Expr :: instMethod */
505 init = transTypes.coerce(attrEnv, tree.getQualifierExpression(),
506 types.erasure(tree.sym.owner.type));
507 init = attr.makeNullCheck(init);
508 break;
509
510 case UNBOUND: /** Type :: instMethod */
511 case STATIC: /** Type :: staticMethod */
512 case TOPLEVEL: /** Top level :: new */
513 case ARRAY_CTOR: /** ArrayType :: new */
514 init = null;
515 break;
516
517 default:
518 throw new InternalError("Should not have an invalid kind");
519 }
520
521 List<JCExpression> indy_args = init==null? List.nil() : translate(List.of(init), localContext.prev);
522
523
524 //build a sam instance using an indy call to the meta-factory
525 result = makeMetafactoryIndyCall(localContext, refSym.asHandle(), indy_args);
526 }
527
528 /**
529 * Translate identifiers within a lambda to the mapped identifier
530 * @param tree
531 */
532 @Override
533 public void visitIdent(JCIdent tree) {
534 if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) {
535 super.visitIdent(tree);
536 } else {
537 int prevPos = make.pos;
538 try {
539 make.at(tree);
540
541 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context;
542 JCTree ltree = lambdaContext.translate(tree);
904
905 MemberReferenceToLambda(JCMemberReference tree, ReferenceTranslationContext localContext, Symbol owner) {
906 this.tree = tree;
907 this.localContext = localContext;
908 this.owner = owner;
909 }
910
911 JCLambda lambda() {
912 int prevPos = make.pos;
913 try {
914 make.at(tree);
915
916 //body generation - this can be either a method call or a
917 //new instance creation expression, depending on the member reference kind
918 VarSymbol rcvr = addParametersReturnReceiver();
919 JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE)
920 ? expressionInvoke(rcvr)
921 : expressionNew();
922
923 JCLambda slam = make.Lambda(params.toList(), expr);
924 slam.target = tree.target;
925 slam.type = tree.type;
926 slam.pos = tree.pos;
927 return slam;
928 } finally {
929 make.at(prevPos);
930 }
931 }
932
933 /**
934 * Generate the parameter list for the converted member reference.
935 *
936 * @return The receiver variable symbol, if any
937 */
938 VarSymbol addParametersReturnReceiver() {
939 Type samDesc = localContext.bridgedRefSig();
940 List<Type> samPTypes = samDesc.getParameterTypes();
941 List<Type> descPTypes = tree.getDescriptorType(types).getParameterTypes();
942
943 // Determine the receiver, if any
1123 indy_args_types.append(arg.type);
1124 }
1125
1126 //finally, compute the type of the indy call
1127 MethodType indyType = new MethodType(indy_args_types.toList(),
1128 tree.type,
1129 List.nil(),
1130 syms.methodClass);
1131
1132 Name metafactoryName = context.needsAltMetafactory() ?
1133 names.altMetafactory : names.metafactory;
1134
1135 if (context.needsAltMetafactory()) {
1136 ListBuffer<Type> markers = new ListBuffer<>();
1137 List<Type> targets = tree.target.isIntersection() ?
1138 types.directSupertypes(tree.target) :
1139 List.nil();
1140 for (Type t : targets) {
1141 t = types.erasure(t);
1142 if (t.tsym != syms.serializableType.tsym &&
1143 t.tsym != tree.type.tsym &&
1144 t.tsym != syms.objectType.tsym) {
1145 markers.append(t);
1146 }
1147 }
1148 int flags = context.isSerializable() ? FLAG_SERIALIZABLE : 0;
1149 boolean hasMarkers = markers.nonEmpty();
1150 boolean hasBridges = context.bridges.nonEmpty();
1151 if (hasMarkers) {
1152 flags |= FLAG_MARKERS;
1153 }
1154 if (hasBridges) {
1155 flags |= FLAG_BRIDGES;
1156 }
1157 staticArgs = staticArgs.append(LoadableConstant.Int(flags));
1158 if (hasMarkers) {
1159 staticArgs = staticArgs.append(LoadableConstant.Int(markers.length()));
1160 staticArgs = staticArgs.appendList(List.convert(LoadableConstant.class, markers.toList()));
1161 }
1162 if (hasBridges) {
1163 staticArgs = staticArgs.append(LoadableConstant.Int(context.bridges.length() - 1));
1164 for (Symbol s : context.bridges) {
1165 Type s_erasure = s.erasure(types);
1166 if (!types.isSameType(s_erasure, samSym.erasure(types))) {
1167 staticArgs = staticArgs.append(((MethodType)s.erasure(types)));
1168 }
1169 }
1170 }
1171 if (context.isSerializable()) {
1172 int prevPos = make.pos;
1173 try {
1174 make.at(kInfo.clazz);
1175 addDeserializationCase(refSym, tree.type, samSym,
1176 tree, staticArgs, indyType);
1177 } finally {
1178 make.at(prevPos);
1179 }
1180 }
1181 }
1182
1183 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args, samSym.name);
1184 }
1185
1186 /**
1187 * Generate an indy method call with given name, type and static bootstrap
1188 * arguments types
1189 */
1190 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
1845 /** the enclosing translation context (set for nested lambdas/mref) */
1846 final TranslationContext<?> prev;
1847
1848 /** list of methods to be bridged by the meta-factory */
1849 final List<Symbol> bridges;
1850
1851 TranslationContext(T tree) {
1852 this.tree = tree;
1853 this.owner = owner(true);
1854 this.depth = frameStack.size() - 1;
1855 this.prev = context();
1856 ClassSymbol csym =
1857 types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.target, ABSTRACT | INTERFACE);
1858 this.bridges = types.functionalInterfaceBridges(csym);
1859 }
1860
1861 /** does this functional expression need to be created using alternate metafactory? */
1862 boolean needsAltMetafactory() {
1863 return tree.target.isIntersection() ||
1864 isSerializable() ||
1865 bridges.length() > 1;
1866 }
1867
1868 /** does this functional expression require serialization support? */
1869 boolean isSerializable() {
1870 if (forceSerializable) {
1871 return true;
1872 }
1873 return types.asSuper(tree.target, syms.serializableType.tsym) != null;
1874 }
1875
1876 /**
1877 * @return Name of the enclosing method to be folded into synthetic
1878 * method name
1879 */
1880 String enclosingMethodName() {
1881 return syntheticMethodNameComponent(owner.name);
1882 }
1883
1884 /**
1885 * @return Method name in a form that can be folded into a
1886 * component of a synthetic method name
1887 */
1888 String syntheticMethodNameComponent(Name name) {
1889 if (name == null) {
1890 return "null";
1891 }
1892 String methodName = name.toString();
1893 if (methodName.equals("<clinit>")) {
1894 methodName = "static";
|
1 /*
2 * Copyright (c) 2010, 2024, 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
121 private final boolean debugLinesOrVars;
122
123 /** dump statistics about lambda method deduplication */
124 private final boolean verboseDeduplication;
125
126 /** deduplicate lambda implementation methods */
127 private final boolean deduplicateLambdas;
128
129 /** lambda proxy is a dynamic nestmate */
130 private final boolean nestmateLambdas;
131
132 /** Flag for alternate metafactories indicating the lambda object is intended to be serializable */
133 public static final int FLAG_SERIALIZABLE = 1 << 0;
134
135 /** Flag for alternate metafactories indicating the lambda object has multiple targets */
136 public static final int FLAG_MARKERS = 1 << 1;
137
138 /** Flag for alternate metafactories indicating the lambda object requires multiple bridges */
139 public static final int FLAG_BRIDGES = 1 << 2;
140
141 /** Flag for alternate metafactories indicating the lambda object is intended to be quotable */
142 public static final int FLAG_QUOTABLE = 1 << 3;
143
144 // <editor-fold defaultstate="collapsed" desc="Instantiating">
145 protected static final Context.Key<LambdaToMethod> unlambdaKey = new Context.Key<>();
146
147 public static LambdaToMethod instance(Context context) {
148 LambdaToMethod instance = context.get(unlambdaKey);
149 if (instance == null) {
150 instance = new LambdaToMethod(context);
151 }
152 return instance;
153 }
154 private LambdaToMethod(Context context) {
155 context.put(unlambdaKey, this);
156 diags = JCDiagnostic.Factory.instance(context);
157 log = Log.instance(context);
158 lower = Lower.instance(context);
159 names = Names.instance(context);
160 syms = Symtab.instance(context);
161 rs = Resolve.instance(context);
162 operators = Operators.instance(context);
163 make = TreeMaker.instance(context);
425 syntheticInits.append(localContext.methodReferenceReceiver);
426 } else if (!sym.isStatic()) {
427 syntheticInits.append(makeThis(
428 sym.owner.enclClass().asType(),
429 localContext.owner.enclClass()));
430 }
431
432 //add captured locals
433 for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) {
434 if (fv != localContext.self) {
435 JCExpression captured_local = make.Ident(fv).setType(fv.type);
436 syntheticInits.append(captured_local);
437 }
438 }
439 // add captured outer this instances (used only when `this' capture itself is illegal)
440 for (Symbol fv : localContext.getSymbolMap(CAPTURED_OUTER_THIS).keySet()) {
441 JCExpression captured_local = make.QualThis(fv.type);
442 syntheticInits.append(captured_local);
443 }
444
445 if (context.isQuotable()) {
446 for (JCExpression capturedArg : tree.codeReflectionInfo.capturedArgs()) {
447 syntheticInits.append(capturedArg);
448 }
449 }
450
451 //then, determine the arguments to the indy call
452 List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev);
453
454 //convert to an invokedynamic call
455 result = makeMetafactoryIndyCall(context, sym.asHandle(), indy_args);
456 }
457
458 // where
459 // Reassign type annotations from the source that should really belong to the lambda
460 private void apportionTypeAnnotations(JCLambda tree,
461 Supplier<List<Attribute.TypeCompound>> source,
462 Consumer<List<Attribute.TypeCompound>> owner,
463 Consumer<List<Attribute.TypeCompound>> lambda) {
464
465 ListBuffer<Attribute.TypeCompound> ownerTypeAnnos = new ListBuffer<>();
466 ListBuffer<Attribute.TypeCompound> lambdaTypeAnnos = new ListBuffer<>();
467
468 for (Attribute.TypeCompound tc : source.get()) {
469 if (tc.position.onLambda == tree) {
470 lambdaTypeAnnos.append(tc);
512
513 case BOUND: /** Expr :: instMethod */
514 init = transTypes.coerce(attrEnv, tree.getQualifierExpression(),
515 types.erasure(tree.sym.owner.type));
516 init = attr.makeNullCheck(init);
517 break;
518
519 case UNBOUND: /** Type :: instMethod */
520 case STATIC: /** Type :: staticMethod */
521 case TOPLEVEL: /** Top level :: new */
522 case ARRAY_CTOR: /** ArrayType :: new */
523 init = null;
524 break;
525
526 default:
527 throw new InternalError("Should not have an invalid kind");
528 }
529
530 List<JCExpression> indy_args = init==null? List.nil() : translate(List.of(init), localContext.prev);
531
532 if (context.isQuotable()) {
533 for (JCExpression capturedArg : tree.codeReflectionInfo.capturedArgs()) {
534 indy_args = indy_args.append(capturedArg);
535 }
536 }
537
538
539 //build a sam instance using an indy call to the meta-factory
540 result = makeMetafactoryIndyCall(localContext, refSym.asHandle(), indy_args);
541 }
542
543 /**
544 * Translate identifiers within a lambda to the mapped identifier
545 * @param tree
546 */
547 @Override
548 public void visitIdent(JCIdent tree) {
549 if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) {
550 super.visitIdent(tree);
551 } else {
552 int prevPos = make.pos;
553 try {
554 make.at(tree);
555
556 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context;
557 JCTree ltree = lambdaContext.translate(tree);
919
920 MemberReferenceToLambda(JCMemberReference tree, ReferenceTranslationContext localContext, Symbol owner) {
921 this.tree = tree;
922 this.localContext = localContext;
923 this.owner = owner;
924 }
925
926 JCLambda lambda() {
927 int prevPos = make.pos;
928 try {
929 make.at(tree);
930
931 //body generation - this can be either a method call or a
932 //new instance creation expression, depending on the member reference kind
933 VarSymbol rcvr = addParametersReturnReceiver();
934 JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE)
935 ? expressionInvoke(rcvr)
936 : expressionNew();
937
938 JCLambda slam = make.Lambda(params.toList(), expr);
939 slam.codeReflectionInfo = tree.codeReflectionInfo;
940 slam.target = tree.target;
941 slam.type = tree.type;
942 slam.pos = tree.pos;
943 return slam;
944 } finally {
945 make.at(prevPos);
946 }
947 }
948
949 /**
950 * Generate the parameter list for the converted member reference.
951 *
952 * @return The receiver variable symbol, if any
953 */
954 VarSymbol addParametersReturnReceiver() {
955 Type samDesc = localContext.bridgedRefSig();
956 List<Type> samPTypes = samDesc.getParameterTypes();
957 List<Type> descPTypes = tree.getDescriptorType(types).getParameterTypes();
958
959 // Determine the receiver, if any
1139 indy_args_types.append(arg.type);
1140 }
1141
1142 //finally, compute the type of the indy call
1143 MethodType indyType = new MethodType(indy_args_types.toList(),
1144 tree.type,
1145 List.nil(),
1146 syms.methodClass);
1147
1148 Name metafactoryName = context.needsAltMetafactory() ?
1149 names.altMetafactory : names.metafactory;
1150
1151 if (context.needsAltMetafactory()) {
1152 ListBuffer<Type> markers = new ListBuffer<>();
1153 List<Type> targets = tree.target.isIntersection() ?
1154 types.directSupertypes(tree.target) :
1155 List.nil();
1156 for (Type t : targets) {
1157 t = types.erasure(t);
1158 if (t.tsym != syms.serializableType.tsym &&
1159 t.tsym != syms.quotableType.tsym &&
1160 t.tsym != tree.type.tsym &&
1161 t.tsym != syms.objectType.tsym) {
1162 markers.append(t);
1163 }
1164 }
1165 int flags = context.isSerializable() ? FLAG_SERIALIZABLE : 0;
1166 flags |= context.isQuotable() ? FLAG_QUOTABLE : 0;
1167 boolean hasMarkers = markers.nonEmpty();
1168 boolean hasBridges = context.bridges.nonEmpty();
1169 if (hasMarkers) {
1170 flags |= FLAG_MARKERS;
1171 }
1172 if (hasBridges) {
1173 flags |= FLAG_BRIDGES;
1174 }
1175 staticArgs = staticArgs.append(LoadableConstant.Int(flags));
1176 if (hasMarkers) {
1177 staticArgs = staticArgs.append(LoadableConstant.Int(markers.length()));
1178 staticArgs = staticArgs.appendList(List.convert(LoadableConstant.class, markers.toList()));
1179 }
1180 if (hasBridges) {
1181 staticArgs = staticArgs.append(LoadableConstant.Int(context.bridges.length() - 1));
1182 for (Symbol s : context.bridges) {
1183 Type s_erasure = s.erasure(types);
1184 if (!types.isSameType(s_erasure, samSym.erasure(types))) {
1185 staticArgs = staticArgs.append(((MethodType)s.erasure(types)));
1186 }
1187 }
1188 }
1189 if (context.isQuotable()) {
1190 VarSymbol reflectField = (VarSymbol)tree.codeReflectionInfo.quotedField();
1191 staticArgs = staticArgs.append(reflectField.asMethodHandle(true));
1192 }
1193 if (context.isSerializable()) {
1194 int prevPos = make.pos;
1195 try {
1196 make.at(kInfo.clazz);
1197 addDeserializationCase(refSym, tree.type, samSym,
1198 tree, staticArgs, indyType);
1199 } finally {
1200 make.at(prevPos);
1201 }
1202 }
1203 }
1204
1205 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args, samSym.name);
1206 }
1207
1208 /**
1209 * Generate an indy method call with given name, type and static bootstrap
1210 * arguments types
1211 */
1212 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
1867 /** the enclosing translation context (set for nested lambdas/mref) */
1868 final TranslationContext<?> prev;
1869
1870 /** list of methods to be bridged by the meta-factory */
1871 final List<Symbol> bridges;
1872
1873 TranslationContext(T tree) {
1874 this.tree = tree;
1875 this.owner = owner(true);
1876 this.depth = frameStack.size() - 1;
1877 this.prev = context();
1878 ClassSymbol csym =
1879 types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.target, ABSTRACT | INTERFACE);
1880 this.bridges = types.functionalInterfaceBridges(csym);
1881 }
1882
1883 /** does this functional expression need to be created using alternate metafactory? */
1884 boolean needsAltMetafactory() {
1885 return tree.target.isIntersection() ||
1886 isSerializable() ||
1887 isQuotable() ||
1888 bridges.length() > 1;
1889 }
1890
1891 /** does this functional expression require serialization support? */
1892 boolean isSerializable() {
1893 if (forceSerializable) {
1894 return true;
1895 }
1896 return types.asSuper(tree.target, syms.serializableType.tsym) != null;
1897 }
1898
1899 boolean isQuotable() {
1900 return tree.codeReflectionInfo != null;
1901 }
1902
1903 /**
1904 * @return Name of the enclosing method to be folded into synthetic
1905 * method name
1906 */
1907 String enclosingMethodName() {
1908 return syntheticMethodNameComponent(owner.name);
1909 }
1910
1911 /**
1912 * @return Method name in a form that can be folded into a
1913 * component of a synthetic method name
1914 */
1915 String syntheticMethodNameComponent(Name name) {
1916 if (name == null) {
1917 return "null";
1918 }
1919 String methodName = name.toString();
1920 if (methodName.equals("<clinit>")) {
1921 methodName = "static";
|