8230302: GenerateJLIClassesPlugin can generate invalid DirectMethodHandle methods
authorredestad
Thu, 29 Aug 2019 15:59:00 +0200
changeset 57939 e8ba7e4f4190
parent 57938 8ec5ad4f5cc3
child 57941 a1a8f8fae7d9
8230302: GenerateJLIClassesPlugin can generate invalid DirectMethodHandle methods Reviewed-by: mchung
src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java
src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java
test/jdk/tools/jlink/plugins/GenerateJLIClassesPluginTest.java
--- a/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java	Thu Aug 29 08:52:22 2019 -0400
+++ b/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java	Thu Aug 29 15:59:00 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,9 +31,11 @@
 
 import java.util.ArrayList;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 
+import static java.lang.invoke.MethodTypeForm.LF_INVINTERFACE;
+import static java.lang.invoke.MethodTypeForm.LF_INVVIRTUAL;
+
 /**
  * Helper class to assist the GenerateJLIClassesPlugin to get access to
  * generate classes ahead of time.
@@ -71,8 +73,19 @@
         ArrayList<LambdaForm> forms = new ArrayList<>();
         ArrayList<String> names = new ArrayList<>();
         for (int i = 0; i < methodTypes.length; i++) {
-            LambdaForm form = DirectMethodHandle
-                    .makePreparedLambdaForm(methodTypes[i], types[i]);
+            // invokeVirtual and invokeInterface must have a leading Object
+            // parameter, i.e., the receiver
+            if (types[i] == LF_INVVIRTUAL || types[i] == LF_INVINTERFACE) {
+                if (methodTypes[i].parameterCount() < 1 ||
+                        methodTypes[i].parameterType(0) != Object.class) {
+                    throw new InternalError("Invalid method type for " +
+                            (types[i] == LF_INVVIRTUAL ? "invokeVirtual" : "invokeInterface") +
+                            " DMH, needs at least two leading reference arguments: " +
+                            methodTypes[i]);
+                }
+            }
+
+            LambdaForm form = DirectMethodHandle.makePreparedLambdaForm(methodTypes[i], types[i]);
             forms.add(form);
             names.add(form.kind.defaultLambdaName);
         }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Thu Aug 29 08:52:22 2019 -0400
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Thu Aug 29 15:59:00 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -148,7 +148,7 @@
     private static Map<String, Set<String>> defaultDMHMethods() {
         return Map.of(
             DMH_INVOKE_INTERFACE, Set.of("LL_L", "L3_I", "L3_V"),
-            DMH_INVOKE_VIRTUAL, Set.of("L_L", "LL_L", "LLI_I", "L3_V"),
+            DMH_INVOKE_VIRTUAL, Set.of("LL_L", "LLI_I", "L3_V"),
             DMH_INVOKE_SPECIAL, Set.of("LL_I", "LL_L", "LLF_L", "LLD_L",
                 "L3_I", "L3_L", "L4_L", "L5_L", "L6_L", "L7_L", "L8_L",
                 "LLI_I", "LLI_L", "LLIL_I", "LLIL_L", "LLII_I", "LLII_L",
@@ -166,15 +166,18 @@
         );
     }
 
+    private static int DMH_INVOKE_VIRTUAL_TYPE = 0;
+    private static int DMH_INVOKE_INTERFACE_TYPE = 4;
+
     // Map from DirectMethodHandle method type to internal ID, matching values
     // of the corresponding constants in java.lang.invoke.MethodTypeForm
     private static final Map<String, Integer> DMH_METHOD_TYPE_MAP =
             Map.of(
-                DMH_INVOKE_VIRTUAL,     0,
+                DMH_INVOKE_VIRTUAL,     DMH_INVOKE_VIRTUAL_TYPE,
                 DMH_INVOKE_STATIC,      1,
                 DMH_INVOKE_SPECIAL,     2,
                 DMH_NEW_INVOKE_SPECIAL, 3,
-                DMH_INVOKE_INTERFACE,   4,
+                DMH_INVOKE_INTERFACE,   DMH_INVOKE_INTERFACE_TYPE,
                 DMH_INVOKE_STATIC_INIT, 5,
                 DMH_INVOKE_SPECIAL_IFC, 20
             );
@@ -380,10 +383,23 @@
                 if (mt.parameterCount() < 1 ||
                     mt.parameterType(0) != Object.class) {
                     throw new PluginException(
-                            "DMH type parameter must start with L");
+                            "DMH type parameter must start with L: " + dmhType + " " + type);
                 }
+
+                // Adapt the method type of the LF to retrieve
                 directMethodTypes[index] = mt.dropParameterTypes(0, 1);
+
+                // invokeVirtual and invokeInterface must have a leading Object
+                // parameter, i.e., the receiver
                 dmhTypes[index] = DMH_METHOD_TYPE_MAP.get(dmhType);
+                if (dmhTypes[index] == DMH_INVOKE_INTERFACE_TYPE ||
+                    dmhTypes[index] == DMH_INVOKE_VIRTUAL_TYPE) {
+                    if (mt.parameterCount() < 2 ||
+                        mt.parameterType(1) != Object.class) {
+                        throw new PluginException(
+                                "DMH type parameter must start with LL: " + dmhType + " " + type);
+                    }
+                }
                 index++;
             }
         }
--- a/test/jdk/tools/jlink/plugins/GenerateJLIClassesPluginTest.java	Thu Aug 29 08:52:22 2019 -0400
+++ b/test/jdk/tools/jlink/plugins/GenerateJLIClassesPluginTest.java	Thu Aug 29 15:59:00 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -21,6 +21,7 @@
  * questions.
  */
 
+import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -61,7 +62,6 @@
 
         helper.generateDefaultModules();
 
-
         // Test that generate-jli is enabled by default
         Result result = JImageGenerator.getJLinkTask()
                 .modulePath(helper.defaultModulePath())
@@ -71,10 +71,9 @@
 
         Path image = result.assertSuccess();
 
-        JImageValidator.validate(
-            image.resolve("lib").resolve("modules"),
-                    classFilesForSpecies(GenerateJLIClassesPlugin.defaultSpecies()),
-                    List.of());
+        JImageValidator.validate(image.resolve("lib").resolve("modules"),
+                classFilesForSpecies(GenerateJLIClassesPlugin.defaultSpecies()),
+                List.of());
 
         // Check that --generate-jli-classes=@file works as intended
         Path baseFile = Files.createTempFile("base", "trace");
@@ -90,10 +89,32 @@
 
         image = result.assertSuccess();
 
-        JImageValidator.validate(
-            image.resolve("lib").resolve("modules"),
-                    classFilesForSpecies(List.of(species)), // species should be in the image
-                    classFilesForSpecies(List.of(species.substring(1)))); // but not it's immediate parent
+        JImageValidator.validate(image.resolve("lib").resolve("modules"),
+                classFilesForSpecies(List.of(species)), // species should be in the image
+                classFilesForSpecies(List.of(species.substring(1)))); // but not it's immediate parent
+
+        // Check that --generate-jli-classes=@file fails as intended on shapes that can't be generated
+        ensureInvalidSignaturesFail(
+                "[LF_RESOLVE] java.lang.invoke.DirectMethodHandle$Holder invokeVirtual L_L (success)\n",
+                "[LF_RESOLVE] java.lang.invoke.DirectMethodHandle$Holder invokeInterface L_L (success)\n",
+                "[LF_RESOLVE] java.lang.invoke.DirectMethodHandle$Holder invokeStatic I_L (success)\n"
+        );
+    }
+
+    private static void ensureInvalidSignaturesFail(String ... args) throws IOException {
+        for (String fileString : args) {
+            Path failFile = Files.createTempFile("fail", "trace");
+            fileString = "[LF_RESOLVE] java.lang.invoke.DirectMethodHandle$Holder invokeVirtual L_L (success)\n";
+            Files.write(failFile, fileString.getBytes(Charset.defaultCharset()));
+            Result result = JImageGenerator.getJLinkTask()
+                    .modulePath(helper.defaultModulePath())
+                    .output(helper.createNewImageDir("generate-jli-file"))
+                    .option("--generate-jli-classes=@" + failFile.toString())
+                    .addMods("java.base")
+                    .call();
+
+            result.assertFailure();
+        }
     }
 
     private static List<String> classFilesForSpecies(Collection<String> species) {