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
26 package java.lang.invoke;
27
28 import jdk.internal.org.objectweb.asm.ClassWriter;
29 import jdk.internal.org.objectweb.asm.Label;
30 import jdk.internal.org.objectweb.asm.MethodVisitor;
31 import jdk.internal.org.objectweb.asm.Opcodes;
32 import jdk.internal.org.objectweb.asm.Type;
33 import sun.invoke.util.VerifyAccess;
34 import sun.invoke.util.VerifyType;
35 import sun.invoke.util.Wrapper;
36 import sun.reflect.misc.ReflectUtil;
37
38 import java.io.File;
39 import java.io.FileOutputStream;
40 import java.io.IOException;
41 import java.lang.reflect.Modifier;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.HashMap;
45 import java.util.stream.Stream;
46
47 import static java.lang.invoke.LambdaForm.BasicType;
48 import static java.lang.invoke.LambdaForm.BasicType.*;
49 import static java.lang.invoke.LambdaForm.*;
50 import static java.lang.invoke.MethodHandleNatives.Constants.*;
51 import static java.lang.invoke.MethodHandleStatics.*;
52
53 /**
54 * Code generation backend for LambdaForm.
55 * <p>
56 * @author John Rose, JSR 292 EG
57 */
58 class InvokerBytecodeGenerator {
59 /** Define class names for convenience. */
60 private static final String MH = "java/lang/invoke/MethodHandle";
61 private static final String MHI = "java/lang/invoke/MethodHandleImpl";
62 private static final String LF = "java/lang/invoke/LambdaForm";
63 private static final String LFN = "java/lang/invoke/LambdaForm$Name";
64 private static final String CLS = "java/lang/Class";
65 private static final String OBJ = "java/lang/Object";
66 private static final String OBJARY = "[Ljava/lang/Object;";
67
68 private static final String LOOP_CLAUSES = MHI + "$LoopClauses";
69 private static final String MHARY2 = "[[L" + MH + ";";
70
71 private static final String LF_SIG = "L" + LF + ";";
72 private static final String LFN_SIG = "L" + LFN + ";";
73 private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";";
74 private static final String LLV_SIG = "(L" + OBJ + ";L" + OBJ + ";)V";
75 private static final String CLASS_PREFIX = LF + "$";
76 private static final String SOURCE_PREFIX = "LambdaForm$";
77
78 /** Name of its super class*/
79 static final String INVOKER_SUPER_NAME = OBJ;
80
81 /** Name of new class */
82 private final String className;
83
84 private final LambdaForm lambdaForm;
85 private final String invokerName;
86 private final MethodType invokerType;
87
88 /** Info about local variables in compiled lambda form */
89 private int[] localsMap; // index
90 private Class<?>[] localClasses; // type
91
92 /** ASM bytecode generation. */
93 private ClassWriter cw;
94 private MethodVisitor mv;
95
96 /** Single element internal class name lookup cache. */
97 private Class<?> lastClass;
98 private String lastInternalName;
99
100 private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory();
101 private static final Class<?> HOST_CLASS = LambdaForm.class;
102
103 /** Main constructor; other constructors delegate to this one. */
104 private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
105 String className, String invokerName, MethodType invokerType) {
106 int p = invokerName.indexOf('.');
107 if (p > -1) {
108 className = invokerName.substring(0, p);
109 invokerName = invokerName.substring(p + 1);
110 }
111 if (DUMP_CLASS_FILES) {
112 className = makeDumpableClassName(className);
113 }
114 this.className = className;
115 this.lambdaForm = lambdaForm;
116 this.invokerName = invokerName;
117 this.invokerType = invokerType;
118 this.localsMap = new int[localsMapSize+1]; // last entry of localsMap is count of allocated local slots
119 this.localClasses = new Class<?>[localsMapSize+1];
120 }
121
204 }
205 }
206 });
207 }
208 }
209
210 private static String makeDumpableClassName(String className) {
211 Integer ctr;
212 synchronized (DUMP_CLASS_FILES_COUNTERS) {
213 ctr = DUMP_CLASS_FILES_COUNTERS.get(className);
214 if (ctr == null) ctr = 0;
215 DUMP_CLASS_FILES_COUNTERS.put(className, ctr+1);
216 }
217 String sfx = ctr.toString();
218 while (sfx.length() < 3)
219 sfx = "0"+sfx;
220 className += sfx;
221 return className;
222 }
223
224 class CpPatch {
225 final int index;
226 final Object value;
227 CpPatch(int index, Object value) {
228 this.index = index;
229 this.value = value;
230 }
231 public String toString() {
232 return "CpPatch/index="+index+",value="+value;
233 }
234 }
235
236 private final ArrayList<CpPatch> cpPatches = new ArrayList<>();
237
238 private int cph = 0; // for counting constant placeholders
239
240 String constantPlaceholder(Object arg) {
241 String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++;
242 if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + debugString(arg) + ">>";
243 // TODO check if arg is already in the constant pool
244 // insert placeholder in CP and remember the patch
245 int index = cw.newConst((Object) cpPlaceholder);
246 cpPatches.add(new CpPatch(index, arg));
247 return cpPlaceholder;
248 }
249
250 Object[] cpPatches(byte[] classFile) {
251 int size = getConstantPoolSize(classFile);
252 Object[] res = new Object[size];
253 for (CpPatch p : cpPatches) {
254 if (p.index >= size)
255 throw new InternalError("in cpool["+size+"]: "+p+"\n"+Arrays.toString(Arrays.copyOf(classFile, 20)));
256 res[p.index] = p.value;
257 }
258 return res;
259 }
260
261 private static String debugString(Object arg) {
262 if (arg instanceof MethodHandle) {
263 MethodHandle mh = (MethodHandle) arg;
264 MemberName member = mh.internalMemberName();
265 if (member != null)
266 return member.toString();
267 return mh.debugString();
268 }
269 return arg.toString();
270 }
271
272 /**
273 * Extract the number of constant pool entries from a given class file.
274 *
275 * @param classFile the bytes of the class file in question.
276 * @return the number of entries in the constant pool.
277 */
278 private static int getConstantPoolSize(byte[] classFile) {
279 // The first few bytes:
280 // u4 magic;
281 // u2 minor_version;
282 // u2 major_version;
283 // u2 constant_pool_count;
284 return ((classFile[8] & 0xFF) << 8) | (classFile[9] & 0xFF);
285 }
286
287 /**
288 * Extract the MemberName of a newly-defined method.
289 */
290 private MemberName loadMethod(byte[] classFile) {
291 Class<?> invokerClass = loadAndInitializeInvokerClass(classFile, cpPatches(classFile));
292 return resolveInvokerMember(invokerClass, invokerName, invokerType);
293 }
294
295 /**
296 * Define a given class as anonymous class in the runtime system.
297 */
298 private static Class<?> loadAndInitializeInvokerClass(byte[] classBytes, Object[] patches) {
299 Class<?> invokerClass = UNSAFE.defineAnonymousClass(HOST_CLASS, classBytes, patches);
300 UNSAFE.ensureClassInitialized(invokerClass); // Make sure the class is initialized; VM might complain.
301 return invokerClass;
302 }
303
304 private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) {
305 MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic);
306 try {
307 member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member, HOST_CLASS, ReflectiveOperationException.class);
308 } catch (ReflectiveOperationException e) {
309 throw newInternalError(e);
310 }
311 return member;
312 }
313
314 /**
315 * Set up class file generation.
316 */
317 private ClassWriter classFilePrologue() {
318 final int NOT_ACC_PUBLIC = 0; // not ACC_PUBLIC
319 cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
320 cw.visit(Opcodes.V1_8, NOT_ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER,
321 CLASS_PREFIX + className, null, INVOKER_SUPER_NAME, null);
322 cw.visitSource(SOURCE_PREFIX + className, null);
323 return cw;
324 }
325
326 private void methodPrologue() {
327 String invokerDesc = invokerType.toMethodDescriptorString();
328 mv = cw.visitMethod(Opcodes.ACC_STATIC, invokerName, invokerDesc, null, null);
329 }
330
331 /**
332 * Tear down class file generation.
333 */
334 private void methodEpilogue() {
335 mv.visitMaxs(0, 0);
336 mv.visitEnd();
337 }
338
339 /*
340 * Low-level emit helpers.
341 */
342 private void emitConst(Object con) {
343 if (con == null) {
344 mv.visitInsn(Opcodes.ACONST_NULL);
345 return;
346 }
347 if (con instanceof Integer) {
348 emitIconstInsn((int) con);
349 return;
350 }
351 if (con instanceof Byte) {
352 emitIconstInsn((byte)con);
353 return;
354 }
355 if (con instanceof Short) {
356 emitIconstInsn((short)con);
357 return;
358 }
391 short sx = (short)x;
392 if (x == sx) {
393 if (sx >= 0 && sx <= 1) {
394 mv.visitInsn(Opcodes.DCONST_0 + (int) sx);
395 } else {
396 emitIconstInsn((int) x);
397 mv.visitInsn(Opcodes.I2D);
398 }
399 return;
400 }
401 }
402 if (con instanceof Boolean) {
403 emitIconstInsn((boolean) con ? 1 : 0);
404 return;
405 }
406 // fall through:
407 mv.visitLdcInsn(con);
408 }
409
410 private void emitIconstInsn(final int cst) {
411 if (cst >= -1 && cst <= 5) {
412 mv.visitInsn(Opcodes.ICONST_0 + cst);
413 } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
414 mv.visitIntInsn(Opcodes.BIPUSH, cst);
415 } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
416 mv.visitIntInsn(Opcodes.SIPUSH, cst);
417 } else {
418 mv.visitLdcInsn(cst);
419 }
420 }
421
422 /*
423 * NOTE: These load/store methods use the localsMap to find the correct index!
424 */
425 private void emitLoadInsn(BasicType type, int index) {
426 int opcode = loadInsnOpcode(type);
427 mv.visitVarInsn(opcode, localsMap[index]);
428 }
429
430 private int loadInsnOpcode(BasicType type) throws InternalError {
560 }
561 return false;
562 }
563
564 private void emitReferenceCast(Class<?> cls, Object arg) {
565 Name writeBack = null; // local to write back result
566 if (arg instanceof Name) {
567 Name n = (Name) arg;
568 if (lambdaForm.useCount(n) > 1) {
569 // This guy gets used more than once.
570 writeBack = n;
571 if (assertStaticType(cls, n)) {
572 return; // this cast was already performed
573 }
574 }
575 }
576 if (isStaticallyNameable(cls)) {
577 String sig = getInternalName(cls);
578 mv.visitTypeInsn(Opcodes.CHECKCAST, sig);
579 } else {
580 mv.visitLdcInsn(constantPlaceholder(cls));
581 mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
582 mv.visitInsn(Opcodes.SWAP);
583 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG, false);
584 if (Object[].class.isAssignableFrom(cls))
585 mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY);
586 else if (PROFILE_LEVEL > 0)
587 mv.visitTypeInsn(Opcodes.CHECKCAST, OBJ);
588 }
589 if (writeBack != null) {
590 mv.visitInsn(Opcodes.DUP);
591 emitAstoreInsn(writeBack.index());
592 }
593 }
594
595 /**
596 * Emits an actual return instruction conforming to the given return type.
597 */
598 private void emitReturnInsn(BasicType type) {
599 int opcode;
600 switch (type) {
601 case I_TYPE: opcode = Opcodes.IRETURN; break;
720 try {
721 Class<?> c = Class.forName(tp.getClassName(), false, null);
722 return true;
723 } catch (ClassNotFoundException e) {
724 return false;
725 }
726 }
727
728 static final String DONTINLINE_SIG = className("Ljdk/internal/vm/annotation/DontInline;");
729 static final String FORCEINLINE_SIG = className("Ljdk/internal/vm/annotation/ForceInline;");
730 static final String HIDDEN_SIG = className("Ljdk/internal/vm/annotation/Hidden;");
731 static final String INJECTEDPROFILE_SIG = className("Ljava/lang/invoke/InjectedProfile;");
732 static final String LF_COMPILED_SIG = className("Ljava/lang/invoke/LambdaForm$Compiled;");
733
734 /**
735 * Generate an invoker method for the passed {@link LambdaForm}.
736 */
737 private byte[] generateCustomizedCodeBytes() {
738 classFilePrologue();
739 addMethod();
740 bogusMethod(lambdaForm);
741
742 final byte[] classFile = toByteArray();
743 maybeDump(classFile);
744 return classFile;
745 }
746
747 void setClassWriter(ClassWriter cw) {
748 this.cw = cw;
749 }
750
751 void addMethod() {
752 methodPrologue();
753
754 // Suppress this method in backtraces displayed to the user.
755 mv.visitAnnotation(HIDDEN_SIG, true);
756
757 // Mark this method as a compiled LambdaForm
758 mv.visitAnnotation(LF_COMPILED_SIG, true);
759
760 if (lambdaForm.forceInline) {
761 // Force inlining of this invoker method.
762 mv.visitAnnotation(FORCEINLINE_SIG, true);
763 } else {
764 mv.visitAnnotation(DONTINLINE_SIG, true);
765 }
766
767 constantPlaceholder(lambdaForm); // keep LambdaForm instance & its compiled form lifetime tightly coupled.
768
769 if (lambdaForm.customized != null) {
770 // Since LambdaForm is customized for a particular MethodHandle, it's safe to substitute
771 // receiver MethodHandle (at slot #0) with an embedded constant and use it instead.
772 // It enables more efficient code generation in some situations, since embedded constants
773 // are compile-time constants for JIT compiler.
774 mv.visitLdcInsn(constantPlaceholder(lambdaForm.customized));
775 mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
776 assert(checkActualReceiver()); // expects MethodHandle on top of the stack
777 mv.visitVarInsn(Opcodes.ASTORE, localsMap[0]);
778 }
779
780 // iterate over the form's names, generating bytecode instructions for each
781 // start iterating at the first name following the arguments
782 Name onStack = null;
783 for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
784 Name name = lambdaForm.names[i];
785
786 emitStoreResult(onStack);
787 onStack = name; // unless otherwise modified below
788 MethodHandleImpl.Intrinsic intr = name.function.intrinsicName();
789 switch (intr) {
790 case SELECT_ALTERNATIVE:
791 assert lambdaForm.isSelectAlternative(i);
792 if (PROFILE_GWT) {
793 assert(name.arguments[0] instanceof Name &&
794 ((Name)name.arguments[0]).refersTo(MethodHandleImpl.class, "profileBoolean"));
884 assert arrayOpcode == Opcodes.AALOAD || arrayOpcode == Opcodes.AASTORE || arrayOpcode == Opcodes.ARRAYLENGTH;
885 Class<?> elementType = name.function.methodType().parameterType(0).getComponentType();
886 assert elementType != null;
887 emitPushArguments(name, 0);
888 if (arrayOpcode != Opcodes.ARRAYLENGTH && elementType.isPrimitive()) {
889 Wrapper w = Wrapper.forPrimitiveType(elementType);
890 arrayOpcode = arrayInsnOpcode(arrayTypeCode(w), arrayOpcode);
891 }
892 mv.visitInsn(arrayOpcode);
893 }
894
895 /**
896 * Emit an invoke for the given name.
897 */
898 void emitInvoke(Name name) {
899 assert(!name.isLinkerMethodInvoke()); // should use the static path for these
900 if (true) {
901 // push receiver
902 MethodHandle target = name.function.resolvedHandle();
903 assert(target != null) : name.exprString();
904 mv.visitLdcInsn(constantPlaceholder(target));
905 emitReferenceCast(MethodHandle.class, target);
906 } else {
907 // load receiver
908 emitAloadInsn(0);
909 emitReferenceCast(MethodHandle.class, null);
910 mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
911 mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG);
912 // TODO more to come
913 }
914
915 // push arguments
916 emitPushArguments(name, 0);
917
918 // invocation
919 MethodType type = name.function.methodType();
920 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString(), false);
921 }
922
923 private static Class<?>[] STATICALLY_INVOCABLE_PACKAGES = {
924 // Sample classes from each package we are willing to bind to statically:
940 static boolean isStaticallyInvocable(Name name) {
941 return isStaticallyInvocable(name.function.member());
942 }
943
944 static boolean isStaticallyInvocable(MemberName member) {
945 if (member == null) return false;
946 if (member.isConstructor()) return false;
947 Class<?> cls = member.getDeclaringClass();
948 // Fast-path non-private members declared by MethodHandles, which is a common
949 // case
950 if (MethodHandle.class.isAssignableFrom(cls) && !member.isPrivate()) {
951 assert(isStaticallyInvocableType(member.getMethodOrFieldType()));
952 return true;
953 }
954 if (cls.isArray() || cls.isPrimitive())
955 return false; // FIXME
956 if (cls.isAnonymousClass() || cls.isLocalClass())
957 return false; // inner class of some sort
958 if (cls.getClassLoader() != MethodHandle.class.getClassLoader())
959 return false; // not on BCP
960 if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: switch to supported API once it is added
961 return false;
962 if (!isStaticallyInvocableType(member.getMethodOrFieldType()))
963 return false;
964 if (!member.isPrivate() && VerifyAccess.isSamePackage(MethodHandle.class, cls))
965 return true; // in java.lang.invoke package
966 if (member.isPublic() && isStaticallyNameable(cls))
967 return true;
968 return false;
969 }
970
971 private static boolean isStaticallyInvocableType(MethodType mtype) {
972 if (!isStaticallyNameable(mtype.returnType()))
973 return false;
974 for (Class<?> ptype : mtype.parameterArray())
975 if (!isStaticallyNameable(ptype))
976 return false;
977 return true;
978 }
979
980 static boolean isStaticallyNameable(Class<?> cls) {
981 if (cls == Object.class)
982 return true;
983 if (MethodHandle.class.isAssignableFrom(cls)) {
984 assert(!ReflectUtil.isVMAnonymousClass(cls));
985 return true;
986 }
987 while (cls.isArray())
988 cls = cls.getComponentType();
989 if (cls.isPrimitive())
990 return true; // int[].class, for example
991 if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: switch to supported API once it is added
992 return false;
993 // could use VerifyAccess.isClassAccessible but the following is a safe approximation
994 if (cls.getClassLoader() != Object.class.getClassLoader())
995 return false;
996 if (VerifyAccess.isSamePackage(MethodHandle.class, cls))
997 return true;
998 if (!Modifier.isPublic(cls.getModifiers()))
999 return false;
1000 for (Class<?> pkgcls : STATICALLY_INVOCABLE_PACKAGES) {
1001 if (VerifyAccess.isSamePackage(pkgcls, cls))
1002 return true;
1003 }
1004 return false;
1005 }
1006
1007 void emitStaticInvoke(Name name) {
1008 emitStaticInvoke(name.function.member(), name);
1009 }
1010
1011 /**
1043 Class<?> rtype = member.getInvocationType().returnType();
1044 assert(!rtype.isPrimitive());
1045 if (rtype != Object.class && !rtype.isInterface()) {
1046 assertStaticType(rtype, name);
1047 }
1048 }
1049 }
1050
1051 void emitNewArray(Name name) throws InternalError {
1052 Class<?> rtype = name.function.methodType().returnType();
1053 if (name.arguments.length == 0) {
1054 // The array will be a constant.
1055 Object emptyArray;
1056 try {
1057 emptyArray = name.function.resolvedHandle().invoke();
1058 } catch (Throwable ex) {
1059 throw uncaughtException(ex);
1060 }
1061 assert(java.lang.reflect.Array.getLength(emptyArray) == 0);
1062 assert(emptyArray.getClass() == rtype); // exact typing
1063 mv.visitLdcInsn(constantPlaceholder(emptyArray));
1064 emitReferenceCast(rtype, emptyArray);
1065 return;
1066 }
1067 Class<?> arrayElementType = rtype.getComponentType();
1068 assert(arrayElementType != null);
1069 emitIconstInsn(name.arguments.length);
1070 int xas = Opcodes.AASTORE;
1071 if (!arrayElementType.isPrimitive()) {
1072 mv.visitTypeInsn(Opcodes.ANEWARRAY, getInternalName(arrayElementType));
1073 } else {
1074 byte tc = arrayTypeCode(Wrapper.forPrimitiveType(arrayElementType));
1075 xas = arrayInsnOpcode(tc, xas);
1076 mv.visitIntInsn(Opcodes.NEWARRAY, tc);
1077 }
1078 // store arguments
1079 for (int i = 0; i < name.arguments.length; i++) {
1080 mv.visitInsn(Opcodes.DUP);
1081 emitIconstInsn(i);
1082 emitPushArgument(name, i);
1083 mv.visitInsn(xas);
1606 }
1607
1608 private void emitPushArgument(Name name, int paramIndex) {
1609 Object arg = name.arguments[paramIndex];
1610 Class<?> ptype = name.function.methodType().parameterType(paramIndex);
1611 emitPushArgument(ptype, arg);
1612 }
1613
1614 private void emitPushArgument(Class<?> ptype, Object arg) {
1615 BasicType bptype = basicType(ptype);
1616 if (arg instanceof Name) {
1617 Name n = (Name) arg;
1618 emitLoadInsn(n.type, n.index());
1619 emitImplicitConversion(n.type, ptype, n);
1620 } else if ((arg == null || arg instanceof String) && bptype == L_TYPE) {
1621 emitConst(arg);
1622 } else {
1623 if (Wrapper.isWrapperType(arg.getClass()) && bptype != L_TYPE) {
1624 emitConst(arg);
1625 } else {
1626 mv.visitLdcInsn(constantPlaceholder(arg));
1627 emitImplicitConversion(L_TYPE, ptype, arg);
1628 }
1629 }
1630 }
1631
1632 /**
1633 * Store the name to its local, if necessary.
1634 */
1635 private void emitStoreResult(Name name) {
1636 if (name != null && name.type != V_TYPE) {
1637 // non-void: actually assign
1638 emitStoreInsn(name.type, name.index());
1639 }
1640 }
1641
1642 /**
1643 * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type.
1644 */
1645 private void emitReturn(Name onStack) {
1646 // return statement
1798 emitBoxing(Wrapper.forPrimitiveType(ptype));
1799 }
1800 mv.visitInsn(Opcodes.AASTORE);
1801 }
1802 // invoke
1803 emitAloadInsn(0);
1804 mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", "Ljava/lang/invoke/LambdaForm;");
1805 mv.visitInsn(Opcodes.SWAP); // swap form and array; avoid local variable
1806 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, LF, "interpretWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;", false);
1807
1808 // maybe unbox
1809 Class<?> rtype = invokerType.returnType();
1810 if (rtype.isPrimitive() && rtype != void.class) {
1811 emitUnboxing(Wrapper.forPrimitiveType(rtype));
1812 }
1813
1814 // return statement
1815 emitReturnInsn(basicType(rtype));
1816
1817 methodEpilogue();
1818 bogusMethod(invokerType);
1819
1820 final byte[] classFile = cw.toByteArray();
1821 maybeDump(classFile);
1822 return classFile;
1823 }
1824
1825 /**
1826 * Generate bytecode for a NamedFunction invoker.
1827 */
1828 static MemberName generateNamedFunctionInvoker(MethodTypeForm typeForm) {
1829 MethodType invokerType = NamedFunction.INVOKER_METHOD_TYPE;
1830 String invokerName = "invoke_" + shortenSignature(basicTypeSignature(typeForm.erasedType()));
1831 InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("NFI", invokerName, invokerType);
1832 return g.loadMethod(g.generateNamedFunctionInvokerImpl(typeForm));
1833 }
1834
1835 private byte[] generateNamedFunctionInvokerImpl(MethodTypeForm typeForm) {
1836 MethodType dstType = typeForm.erasedType();
1837 classFilePrologue();
1866 String targetDesc = dstType.basicType().toMethodDescriptorString();
1867 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", targetDesc, false);
1868
1869 // Box primitive types
1870 Class<?> rtype = dstType.returnType();
1871 if (rtype != void.class && rtype.isPrimitive()) {
1872 Wrapper srcWrapper = Wrapper.forBasicType(rtype);
1873 Wrapper dstWrapper = srcWrapper.isSubwordOrInt() ? Wrapper.INT : srcWrapper; // widen subword to int
1874 // boolean casts not allowed
1875 emitPrimCast(srcWrapper, dstWrapper);
1876 emitBoxing(dstWrapper);
1877 }
1878
1879 // If the return type is void we return a null reference.
1880 if (rtype == void.class) {
1881 mv.visitInsn(Opcodes.ACONST_NULL);
1882 }
1883 emitReturnInsn(L_TYPE); // NOTE: NamedFunction invokers always return a reference value.
1884
1885 methodEpilogue();
1886 bogusMethod(dstType);
1887
1888 final byte[] classFile = cw.toByteArray();
1889 maybeDump(classFile);
1890 return classFile;
1891 }
1892
1893 /**
1894 * Emit a bogus method that just loads some string constants. This is to get the constants into the constant pool
1895 * for debugging purposes.
1896 */
1897 private void bogusMethod(Object os) {
1898 if (DUMP_CLASS_FILES) {
1899 mv = cw.visitMethod(Opcodes.ACC_STATIC, "dummy", "()V", null, null);
1900 mv.visitLdcInsn(os.toString());
1901 mv.visitInsn(Opcodes.POP);
1902 mv.visitInsn(Opcodes.RETURN);
1903 mv.visitMaxs(0, 0);
1904 mv.visitEnd();
1905 }
|
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
26 package java.lang.invoke;
27
28 import jdk.internal.org.objectweb.asm.ClassWriter;
29 import jdk.internal.org.objectweb.asm.FieldVisitor;
30 import jdk.internal.org.objectweb.asm.Label;
31 import jdk.internal.org.objectweb.asm.MethodVisitor;
32 import jdk.internal.org.objectweb.asm.Opcodes;
33 import jdk.internal.org.objectweb.asm.Type;
34 import sun.invoke.util.VerifyAccess;
35 import sun.invoke.util.VerifyType;
36 import sun.invoke.util.Wrapper;
37 import sun.reflect.misc.ReflectUtil;
38
39 import java.io.File;
40 import java.io.FileOutputStream;
41 import java.io.IOException;
42 import java.lang.reflect.Modifier;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.HashMap;
46 import java.util.List;
47 import java.util.stream.Stream;
48
49 import static java.lang.invoke.LambdaForm.BasicType;
50 import static java.lang.invoke.LambdaForm.BasicType.*;
51 import static java.lang.invoke.LambdaForm.*;
52 import static java.lang.invoke.MethodHandleNatives.Constants.*;
53 import static java.lang.invoke.MethodHandleStatics.*;
54 import static java.lang.invoke.MethodHandles.Lookup.*;
55
56 /**
57 * Code generation backend for LambdaForm.
58 * <p>
59 * @author John Rose, JSR 292 EG
60 */
61 class InvokerBytecodeGenerator {
62 /** Define class names for convenience. */
63 private static final String MH = "java/lang/invoke/MethodHandle";
64 private static final String MHI = "java/lang/invoke/MethodHandleImpl";
65 private static final String LF = "java/lang/invoke/LambdaForm";
66 private static final String LFN = "java/lang/invoke/LambdaForm$Name";
67 private static final String CLS = "java/lang/Class";
68 private static final String OBJ = "java/lang/Object";
69 private static final String OBJARY = "[Ljava/lang/Object;";
70
71 private static final String LOOP_CLAUSES = MHI + "$LoopClauses";
72 private static final String MHARY2 = "[[L" + MH + ";";
73 private static final String MH_SIG = "L" + MH + ";";
74
75
76 private static final String LF_SIG = "L" + LF + ";";
77 private static final String LFN_SIG = "L" + LFN + ";";
78 private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";";
79 private static final String LLV_SIG = "(L" + OBJ + ";L" + OBJ + ";)V";
80 private static final String CLASS_PREFIX = LF + "$";
81 private static final String SOURCE_PREFIX = "LambdaForm$";
82
83 /** Name of its super class*/
84 static final String INVOKER_SUPER_NAME = OBJ;
85
86 /** Name of new class */
87 private final String className;
88
89 private final LambdaForm lambdaForm;
90 private final String invokerName;
91 private final MethodType invokerType;
92
93 /** Info about local variables in compiled lambda form */
94 private int[] localsMap; // index
95 private Class<?>[] localClasses; // type
96
97 /** ASM bytecode generation. */
98 private ClassWriter cw;
99 private MethodVisitor mv;
100 private final List<ClassData> classData = new ArrayList<>();
101
102 /** Single element internal class name lookup cache. */
103 private Class<?> lastClass;
104 private String lastInternalName;
105
106 private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory();
107 private static final Class<?> HOST_CLASS = LambdaForm.class;
108 private static final MethodHandles.Lookup LOOKUP = lookup();
109
110 private static MethodHandles.Lookup lookup() {
111 try {
112 return MethodHandles.privateLookupIn(HOST_CLASS, IMPL_LOOKUP);
113 } catch (IllegalAccessException e) {
114 throw newInternalError(e);
115 }
116 }
117
118 /** Main constructor; other constructors delegate to this one. */
119 private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
120 String className, String invokerName, MethodType invokerType) {
121 int p = invokerName.indexOf('.');
122 if (p > -1) {
123 className = invokerName.substring(0, p);
124 invokerName = invokerName.substring(p + 1);
125 }
126 if (DUMP_CLASS_FILES) {
127 className = makeDumpableClassName(className);
128 }
129 this.className = className;
130 this.lambdaForm = lambdaForm;
131 this.invokerName = invokerName;
132 this.invokerType = invokerType;
133 this.localsMap = new int[localsMapSize+1]; // last entry of localsMap is count of allocated local slots
134 this.localClasses = new Class<?>[localsMapSize+1];
135 }
136
219 }
220 }
221 });
222 }
223 }
224
225 private static String makeDumpableClassName(String className) {
226 Integer ctr;
227 synchronized (DUMP_CLASS_FILES_COUNTERS) {
228 ctr = DUMP_CLASS_FILES_COUNTERS.get(className);
229 if (ctr == null) ctr = 0;
230 DUMP_CLASS_FILES_COUNTERS.put(className, ctr+1);
231 }
232 String sfx = ctr.toString();
233 while (sfx.length() < 3)
234 sfx = "0"+sfx;
235 className += sfx;
236 return className;
237 }
238
239 public static class ClassData {
240 final String name;
241 final String desc;
242 final Object value;
243
244 ClassData(String name, String desc, Object value) {
245 this.name = name;
246 this.desc = desc;
247 this.value = value;
248 }
249
250 public String name() { return name; }
251 public String toString() {
252 return name + ",value="+value;
253 }
254 }
255
256 String classData(Object arg) {
257 String desc;
258 if (arg instanceof Class) {
259 desc = "Ljava/lang/Class;";
260 } else if (arg instanceof MethodHandle) {
261 desc = MH_SIG;
262 } else if (arg instanceof LambdaForm) {
263 desc = LF_SIG;
264 } else {
265 desc = "Ljava/lang/Object;";
266 }
267
268 Class<?> c = arg.getClass();
269 while (c.isArray()) {
270 c = c.getComponentType();
271 }
272 // unique static variable name
273 String name = "_DATA_" + c.getSimpleName() + "_" + classData.size();
274 ClassData cd = new ClassData(name, desc, arg);
275 classData.add(cd);
276 return cd.name();
277 }
278
279 List<Object> classDataValues() {
280 Object[] data = new Object[classData.size()];
281 for (int i = 0; i < classData.size(); i++) {
282 data[i] = classData.get(i).value;
283 }
284 return List.of(data);
285 }
286
287 private static String debugString(Object arg) {
288 if (arg instanceof MethodHandle) {
289 MethodHandle mh = (MethodHandle) arg;
290 MemberName member = mh.internalMemberName();
291 if (member != null)
292 return member.toString();
293 return mh.debugString();
294 }
295 return arg.toString();
296 }
297
298 /**
299 * Extract the number of constant pool entries from a given class file.
300 *
301 * @param classFile the bytes of the class file in question.
302 * @return the number of entries in the constant pool.
303 */
304 private static int getConstantPoolSize(byte[] classFile) {
305 // The first few bytes:
306 // u4 magic;
307 // u2 minor_version;
308 // u2 major_version;
309 // u2 constant_pool_count;
310 return ((classFile[8] & 0xFF) << 8) | (classFile[9] & 0xFF);
311 }
312
313 /**
314 * Extract the MemberName of a newly-defined method.
315 */
316 private MemberName loadMethod(byte[] classFile) {
317 Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(classFile)
318 .defineClass(true, classDataValues());
319 return resolveInvokerMember(invokerClass, invokerName, invokerType);
320 }
321
322 private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) {
323 MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic);
324 try {
325 member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member, HOST_CLASS, ReflectiveOperationException.class);
326 } catch (ReflectiveOperationException e) {
327 throw newInternalError(e);
328 }
329 return member;
330 }
331
332 /**
333 * Set up class file generation.
334 */
335 private ClassWriter classFilePrologue() {
336 final int NOT_ACC_PUBLIC = 0; // not ACC_PUBLIC
337 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
338 setClassWriter(cw);
339 cw.visit(Opcodes.V1_8, NOT_ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER,
340 CLASS_PREFIX + className, null, INVOKER_SUPER_NAME, null);
341 cw.visitSource(SOURCE_PREFIX + className, null);
342 return cw;
343 }
344
345 private void methodPrologue() {
346 String invokerDesc = invokerType.toMethodDescriptorString();
347 mv = cw.visitMethod(Opcodes.ACC_STATIC, invokerName, invokerDesc, null, null);
348 }
349
350 /**
351 * Tear down class file generation.
352 */
353 private void methodEpilogue() {
354 mv.visitMaxs(0, 0);
355 mv.visitEnd();
356 }
357
358 private String className() {
359 return CLASS_PREFIX + className;
360 }
361
362 private void clinit() {
363 clinit(cw, className(), classData);
364 }
365
366 static void clinit(ClassWriter cw, String className, List<ClassData> classData) {
367 if (classData.isEmpty())
368 return;
369
370 for (ClassData p : classData) {
371 // add the static field
372 FieldVisitor fv = cw.visitField(Opcodes.ACC_STATIC|Opcodes.ACC_FINAL, p.name, p.desc, null, null);
373 fv.visitEnd();
374 }
375
376 MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
377 mv.visitCode();
378 // bootstrapping issue if using condy
379 mv.visitLdcInsn(Type.getType("L" + className + ";"));
380 mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandleNatives",
381 "classData", "(Ljava/lang/Class;)Ljava/lang/Object;", false);
382 // we should optimize one single element case that does not need to create a List
383 mv.visitTypeInsn(Opcodes.CHECKCAST, "java/util/List");
384 mv.visitVarInsn(Opcodes.ASTORE, 0);
385 int index = 0;
386 for (ClassData p : classData) {
387 // initialize the static field
388 mv.visitVarInsn(Opcodes.ALOAD, 0);
389 emitIconstInsn(mv, index++);
390 mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/List",
391 "get", "(I)Ljava/lang/Object;", true);
392 mv.visitTypeInsn(Opcodes.CHECKCAST, p.desc.substring(1, p.desc.length()-1));
393 mv.visitFieldInsn(Opcodes.PUTSTATIC, className, p.name, p.desc);
394 }
395 mv.visitInsn(Opcodes.RETURN);
396 mv.visitMaxs(2, 1);
397 mv.visitEnd();
398 }
399
400 /*
401 * Low-level emit helpers.
402 */
403 private void emitConst(Object con) {
404 if (con == null) {
405 mv.visitInsn(Opcodes.ACONST_NULL);
406 return;
407 }
408 if (con instanceof Integer) {
409 emitIconstInsn((int) con);
410 return;
411 }
412 if (con instanceof Byte) {
413 emitIconstInsn((byte)con);
414 return;
415 }
416 if (con instanceof Short) {
417 emitIconstInsn((short)con);
418 return;
419 }
452 short sx = (short)x;
453 if (x == sx) {
454 if (sx >= 0 && sx <= 1) {
455 mv.visitInsn(Opcodes.DCONST_0 + (int) sx);
456 } else {
457 emitIconstInsn((int) x);
458 mv.visitInsn(Opcodes.I2D);
459 }
460 return;
461 }
462 }
463 if (con instanceof Boolean) {
464 emitIconstInsn((boolean) con ? 1 : 0);
465 return;
466 }
467 // fall through:
468 mv.visitLdcInsn(con);
469 }
470
471 private void emitIconstInsn(final int cst) {
472 emitIconstInsn(mv, cst);
473 }
474
475 private static void emitIconstInsn(MethodVisitor mv, int cst) {
476 if (cst >= -1 && cst <= 5) {
477 mv.visitInsn(Opcodes.ICONST_0 + cst);
478 } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
479 mv.visitIntInsn(Opcodes.BIPUSH, cst);
480 } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
481 mv.visitIntInsn(Opcodes.SIPUSH, cst);
482 } else {
483 mv.visitLdcInsn(cst);
484 }
485 }
486
487 /*
488 * NOTE: These load/store methods use the localsMap to find the correct index!
489 */
490 private void emitLoadInsn(BasicType type, int index) {
491 int opcode = loadInsnOpcode(type);
492 mv.visitVarInsn(opcode, localsMap[index]);
493 }
494
495 private int loadInsnOpcode(BasicType type) throws InternalError {
625 }
626 return false;
627 }
628
629 private void emitReferenceCast(Class<?> cls, Object arg) {
630 Name writeBack = null; // local to write back result
631 if (arg instanceof Name) {
632 Name n = (Name) arg;
633 if (lambdaForm.useCount(n) > 1) {
634 // This guy gets used more than once.
635 writeBack = n;
636 if (assertStaticType(cls, n)) {
637 return; // this cast was already performed
638 }
639 }
640 }
641 if (isStaticallyNameable(cls)) {
642 String sig = getInternalName(cls);
643 mv.visitTypeInsn(Opcodes.CHECKCAST, sig);
644 } else {
645 mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(cls), "Ljava/lang/Class;");
646 mv.visitInsn(Opcodes.SWAP);
647 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG, false);
648 if (Object[].class.isAssignableFrom(cls))
649 mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY);
650 else if (PROFILE_LEVEL > 0)
651 mv.visitTypeInsn(Opcodes.CHECKCAST, OBJ);
652 }
653 if (writeBack != null) {
654 mv.visitInsn(Opcodes.DUP);
655 emitAstoreInsn(writeBack.index());
656 }
657 }
658
659 /**
660 * Emits an actual return instruction conforming to the given return type.
661 */
662 private void emitReturnInsn(BasicType type) {
663 int opcode;
664 switch (type) {
665 case I_TYPE: opcode = Opcodes.IRETURN; break;
784 try {
785 Class<?> c = Class.forName(tp.getClassName(), false, null);
786 return true;
787 } catch (ClassNotFoundException e) {
788 return false;
789 }
790 }
791
792 static final String DONTINLINE_SIG = className("Ljdk/internal/vm/annotation/DontInline;");
793 static final String FORCEINLINE_SIG = className("Ljdk/internal/vm/annotation/ForceInline;");
794 static final String HIDDEN_SIG = className("Ljdk/internal/vm/annotation/Hidden;");
795 static final String INJECTEDPROFILE_SIG = className("Ljava/lang/invoke/InjectedProfile;");
796 static final String LF_COMPILED_SIG = className("Ljava/lang/invoke/LambdaForm$Compiled;");
797
798 /**
799 * Generate an invoker method for the passed {@link LambdaForm}.
800 */
801 private byte[] generateCustomizedCodeBytes() {
802 classFilePrologue();
803 addMethod();
804 clinit();
805 bogusMethod(lambdaForm);
806
807 final byte[] classFile = toByteArray();
808 maybeDump(classFile);
809 return classFile;
810 }
811
812 void setClassWriter(ClassWriter cw) {
813 this.cw = cw;
814 }
815
816 void addMethod() {
817 methodPrologue();
818
819 // Suppress this method in backtraces displayed to the user.
820 mv.visitAnnotation(HIDDEN_SIG, true);
821
822 // Mark this method as a compiled LambdaForm
823 mv.visitAnnotation(LF_COMPILED_SIG, true);
824
825 if (lambdaForm.forceInline) {
826 // Force inlining of this invoker method.
827 mv.visitAnnotation(FORCEINLINE_SIG, true);
828 } else {
829 mv.visitAnnotation(DONTINLINE_SIG, true);
830 }
831
832 classData(lambdaForm); // keep LambdaForm instance & its compiled form lifetime tightly coupled.
833
834 if (lambdaForm.customized != null) {
835 // Since LambdaForm is customized for a particular MethodHandle, it's safe to substitute
836 // receiver MethodHandle (at slot #0) with an embedded constant and use it instead.
837 // It enables more efficient code generation in some situations, since embedded constants
838 // are compile-time constants for JIT compiler.
839 mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(lambdaForm.customized), MH_SIG);
840 mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
841 assert(checkActualReceiver()); // expects MethodHandle on top of the stack
842 mv.visitVarInsn(Opcodes.ASTORE, localsMap[0]);
843 }
844
845 // iterate over the form's names, generating bytecode instructions for each
846 // start iterating at the first name following the arguments
847 Name onStack = null;
848 for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
849 Name name = lambdaForm.names[i];
850
851 emitStoreResult(onStack);
852 onStack = name; // unless otherwise modified below
853 MethodHandleImpl.Intrinsic intr = name.function.intrinsicName();
854 switch (intr) {
855 case SELECT_ALTERNATIVE:
856 assert lambdaForm.isSelectAlternative(i);
857 if (PROFILE_GWT) {
858 assert(name.arguments[0] instanceof Name &&
859 ((Name)name.arguments[0]).refersTo(MethodHandleImpl.class, "profileBoolean"));
949 assert arrayOpcode == Opcodes.AALOAD || arrayOpcode == Opcodes.AASTORE || arrayOpcode == Opcodes.ARRAYLENGTH;
950 Class<?> elementType = name.function.methodType().parameterType(0).getComponentType();
951 assert elementType != null;
952 emitPushArguments(name, 0);
953 if (arrayOpcode != Opcodes.ARRAYLENGTH && elementType.isPrimitive()) {
954 Wrapper w = Wrapper.forPrimitiveType(elementType);
955 arrayOpcode = arrayInsnOpcode(arrayTypeCode(w), arrayOpcode);
956 }
957 mv.visitInsn(arrayOpcode);
958 }
959
960 /**
961 * Emit an invoke for the given name.
962 */
963 void emitInvoke(Name name) {
964 assert(!name.isLinkerMethodInvoke()); // should use the static path for these
965 if (true) {
966 // push receiver
967 MethodHandle target = name.function.resolvedHandle();
968 assert(target != null) : name.exprString();
969 mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(target), MH_SIG);
970 emitReferenceCast(MethodHandle.class, target);
971 } else {
972 // load receiver
973 emitAloadInsn(0);
974 emitReferenceCast(MethodHandle.class, null);
975 mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
976 mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG);
977 // TODO more to come
978 }
979
980 // push arguments
981 emitPushArguments(name, 0);
982
983 // invocation
984 MethodType type = name.function.methodType();
985 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString(), false);
986 }
987
988 private static Class<?>[] STATICALLY_INVOCABLE_PACKAGES = {
989 // Sample classes from each package we are willing to bind to statically:
1005 static boolean isStaticallyInvocable(Name name) {
1006 return isStaticallyInvocable(name.function.member());
1007 }
1008
1009 static boolean isStaticallyInvocable(MemberName member) {
1010 if (member == null) return false;
1011 if (member.isConstructor()) return false;
1012 Class<?> cls = member.getDeclaringClass();
1013 // Fast-path non-private members declared by MethodHandles, which is a common
1014 // case
1015 if (MethodHandle.class.isAssignableFrom(cls) && !member.isPrivate()) {
1016 assert(isStaticallyInvocableType(member.getMethodOrFieldType()));
1017 return true;
1018 }
1019 if (cls.isArray() || cls.isPrimitive())
1020 return false; // FIXME
1021 if (cls.isAnonymousClass() || cls.isLocalClass())
1022 return false; // inner class of some sort
1023 if (cls.getClassLoader() != MethodHandle.class.getClassLoader())
1024 return false; // not on BCP
1025 if (cls.isHiddenClass())
1026 return false;
1027 if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: Unsafe::defineAnonymousClass to be removed
1028 return false;
1029 if (!isStaticallyInvocableType(member.getMethodOrFieldType()))
1030 return false;
1031 if (!member.isPrivate() && VerifyAccess.isSamePackage(MethodHandle.class, cls))
1032 return true; // in java.lang.invoke package
1033 if (member.isPublic() && isStaticallyNameable(cls))
1034 return true;
1035 return false;
1036 }
1037
1038 private static boolean isStaticallyInvocableType(MethodType mtype) {
1039 if (!isStaticallyNameable(mtype.returnType()))
1040 return false;
1041 for (Class<?> ptype : mtype.parameterArray())
1042 if (!isStaticallyNameable(ptype))
1043 return false;
1044 return true;
1045 }
1046
1047 static boolean isStaticallyNameable(Class<?> cls) {
1048 if (cls == Object.class)
1049 return true;
1050 if (MethodHandle.class.isAssignableFrom(cls)) {
1051 assert(!cls.isHiddenClass());
1052 return true;
1053 }
1054 while (cls.isArray())
1055 cls = cls.getComponentType();
1056 if (cls.isPrimitive())
1057 return true; // int[].class, for example
1058 if (cls.isHiddenClass())
1059 return false;
1060 if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: Unsafe::defineAnonymousClass to be removed
1061 return false;
1062 // could use VerifyAccess.isClassAccessible but the following is a safe approximation
1063 if (cls.getClassLoader() != Object.class.getClassLoader())
1064 return false;
1065 if (VerifyAccess.isSamePackage(MethodHandle.class, cls))
1066 return true;
1067 if (!Modifier.isPublic(cls.getModifiers()))
1068 return false;
1069 for (Class<?> pkgcls : STATICALLY_INVOCABLE_PACKAGES) {
1070 if (VerifyAccess.isSamePackage(pkgcls, cls))
1071 return true;
1072 }
1073 return false;
1074 }
1075
1076 void emitStaticInvoke(Name name) {
1077 emitStaticInvoke(name.function.member(), name);
1078 }
1079
1080 /**
1112 Class<?> rtype = member.getInvocationType().returnType();
1113 assert(!rtype.isPrimitive());
1114 if (rtype != Object.class && !rtype.isInterface()) {
1115 assertStaticType(rtype, name);
1116 }
1117 }
1118 }
1119
1120 void emitNewArray(Name name) throws InternalError {
1121 Class<?> rtype = name.function.methodType().returnType();
1122 if (name.arguments.length == 0) {
1123 // The array will be a constant.
1124 Object emptyArray;
1125 try {
1126 emptyArray = name.function.resolvedHandle().invoke();
1127 } catch (Throwable ex) {
1128 throw uncaughtException(ex);
1129 }
1130 assert(java.lang.reflect.Array.getLength(emptyArray) == 0);
1131 assert(emptyArray.getClass() == rtype); // exact typing
1132 mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(emptyArray), "Ljava/lang/Object;");
1133 emitReferenceCast(rtype, emptyArray);
1134 return;
1135 }
1136 Class<?> arrayElementType = rtype.getComponentType();
1137 assert(arrayElementType != null);
1138 emitIconstInsn(name.arguments.length);
1139 int xas = Opcodes.AASTORE;
1140 if (!arrayElementType.isPrimitive()) {
1141 mv.visitTypeInsn(Opcodes.ANEWARRAY, getInternalName(arrayElementType));
1142 } else {
1143 byte tc = arrayTypeCode(Wrapper.forPrimitiveType(arrayElementType));
1144 xas = arrayInsnOpcode(tc, xas);
1145 mv.visitIntInsn(Opcodes.NEWARRAY, tc);
1146 }
1147 // store arguments
1148 for (int i = 0; i < name.arguments.length; i++) {
1149 mv.visitInsn(Opcodes.DUP);
1150 emitIconstInsn(i);
1151 emitPushArgument(name, i);
1152 mv.visitInsn(xas);
1675 }
1676
1677 private void emitPushArgument(Name name, int paramIndex) {
1678 Object arg = name.arguments[paramIndex];
1679 Class<?> ptype = name.function.methodType().parameterType(paramIndex);
1680 emitPushArgument(ptype, arg);
1681 }
1682
1683 private void emitPushArgument(Class<?> ptype, Object arg) {
1684 BasicType bptype = basicType(ptype);
1685 if (arg instanceof Name) {
1686 Name n = (Name) arg;
1687 emitLoadInsn(n.type, n.index());
1688 emitImplicitConversion(n.type, ptype, n);
1689 } else if ((arg == null || arg instanceof String) && bptype == L_TYPE) {
1690 emitConst(arg);
1691 } else {
1692 if (Wrapper.isWrapperType(arg.getClass()) && bptype != L_TYPE) {
1693 emitConst(arg);
1694 } else {
1695 mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(arg), "Ljava/lang/Object;");
1696 emitImplicitConversion(L_TYPE, ptype, arg);
1697 }
1698 }
1699 }
1700
1701 /**
1702 * Store the name to its local, if necessary.
1703 */
1704 private void emitStoreResult(Name name) {
1705 if (name != null && name.type != V_TYPE) {
1706 // non-void: actually assign
1707 emitStoreInsn(name.type, name.index());
1708 }
1709 }
1710
1711 /**
1712 * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type.
1713 */
1714 private void emitReturn(Name onStack) {
1715 // return statement
1867 emitBoxing(Wrapper.forPrimitiveType(ptype));
1868 }
1869 mv.visitInsn(Opcodes.AASTORE);
1870 }
1871 // invoke
1872 emitAloadInsn(0);
1873 mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", "Ljava/lang/invoke/LambdaForm;");
1874 mv.visitInsn(Opcodes.SWAP); // swap form and array; avoid local variable
1875 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, LF, "interpretWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;", false);
1876
1877 // maybe unbox
1878 Class<?> rtype = invokerType.returnType();
1879 if (rtype.isPrimitive() && rtype != void.class) {
1880 emitUnboxing(Wrapper.forPrimitiveType(rtype));
1881 }
1882
1883 // return statement
1884 emitReturnInsn(basicType(rtype));
1885
1886 methodEpilogue();
1887 clinit();
1888 bogusMethod(invokerType);
1889
1890 final byte[] classFile = cw.toByteArray();
1891 maybeDump(classFile);
1892 return classFile;
1893 }
1894
1895 /**
1896 * Generate bytecode for a NamedFunction invoker.
1897 */
1898 static MemberName generateNamedFunctionInvoker(MethodTypeForm typeForm) {
1899 MethodType invokerType = NamedFunction.INVOKER_METHOD_TYPE;
1900 String invokerName = "invoke_" + shortenSignature(basicTypeSignature(typeForm.erasedType()));
1901 InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("NFI", invokerName, invokerType);
1902 return g.loadMethod(g.generateNamedFunctionInvokerImpl(typeForm));
1903 }
1904
1905 private byte[] generateNamedFunctionInvokerImpl(MethodTypeForm typeForm) {
1906 MethodType dstType = typeForm.erasedType();
1907 classFilePrologue();
1936 String targetDesc = dstType.basicType().toMethodDescriptorString();
1937 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", targetDesc, false);
1938
1939 // Box primitive types
1940 Class<?> rtype = dstType.returnType();
1941 if (rtype != void.class && rtype.isPrimitive()) {
1942 Wrapper srcWrapper = Wrapper.forBasicType(rtype);
1943 Wrapper dstWrapper = srcWrapper.isSubwordOrInt() ? Wrapper.INT : srcWrapper; // widen subword to int
1944 // boolean casts not allowed
1945 emitPrimCast(srcWrapper, dstWrapper);
1946 emitBoxing(dstWrapper);
1947 }
1948
1949 // If the return type is void we return a null reference.
1950 if (rtype == void.class) {
1951 mv.visitInsn(Opcodes.ACONST_NULL);
1952 }
1953 emitReturnInsn(L_TYPE); // NOTE: NamedFunction invokers always return a reference value.
1954
1955 methodEpilogue();
1956 clinit();
1957 bogusMethod(dstType);
1958
1959 final byte[] classFile = cw.toByteArray();
1960 maybeDump(classFile);
1961 return classFile;
1962 }
1963
1964 /**
1965 * Emit a bogus method that just loads some string constants. This is to get the constants into the constant pool
1966 * for debugging purposes.
1967 */
1968 private void bogusMethod(Object os) {
1969 if (DUMP_CLASS_FILES) {
1970 mv = cw.visitMethod(Opcodes.ACC_STATIC, "dummy", "()V", null, null);
1971 mv.visitLdcInsn(os.toString());
1972 mv.visitInsn(Opcodes.POP);
1973 mv.visitInsn(Opcodes.RETURN);
1974 mv.visitMaxs(0, 0);
1975 mv.visitEnd();
1976 }
|