# HG changeset patch # User redestad # Date 1567087140 -7200 # Node ID e8ba7e4f41900326f4814fada59146447b5af137 # Parent 8ec5ad4f5cc31ab88205ad1921dba5892393a8ec 8230302: GenerateJLIClassesPlugin can generate invalid DirectMethodHandle methods Reviewed-by: mchung diff -r 8ec5ad4f5cc3 -r e8ba7e4f4190 src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.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 forms = new ArrayList<>(); ArrayList 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); } diff -r 8ec5ad4f5cc3 -r e8ba7e4f4190 src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java --- 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> 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 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++; } } diff -r 8ec5ad4f5cc3 -r e8ba7e4f4190 test/jdk/tools/jlink/plugins/GenerateJLIClassesPluginTest.java --- 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 classFilesForSpecies(Collection species) {