8230302: GenerateJLIClassesPlugin can generate invalid DirectMethodHandle methods

Reviewed-by: mchung
This commit is contained in:
Claes Redestad 2019-08-29 15:59:00 +02:00
parent e2287af876
commit b4c63048bb
3 changed files with 69 additions and 19 deletions

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -31,9 +31,11 @@ import sun.invoke.util.Wrapper;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; 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 * Helper class to assist the GenerateJLIClassesPlugin to get access to
* generate classes ahead of time. * generate classes ahead of time.
@ -71,8 +73,19 @@ class GenerateJLIClassesHelper {
ArrayList<LambdaForm> forms = new ArrayList<>(); ArrayList<LambdaForm> forms = new ArrayList<>();
ArrayList<String> names = new ArrayList<>(); ArrayList<String> names = new ArrayList<>();
for (int i = 0; i < methodTypes.length; i++) { for (int i = 0; i < methodTypes.length; i++) {
LambdaForm form = DirectMethodHandle // invokeVirtual and invokeInterface must have a leading Object
.makePreparedLambdaForm(methodTypes[i], types[i]); // 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); forms.add(form);
names.add(form.kind.defaultLambdaName); names.add(form.kind.defaultLambdaName);
} }

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -148,7 +148,7 @@ public final class GenerateJLIClassesPlugin implements Plugin {
private static Map<String, Set<String>> defaultDMHMethods() { private static Map<String, Set<String>> defaultDMHMethods() {
return Map.of( return Map.of(
DMH_INVOKE_INTERFACE, Set.of("LL_L", "L3_I", "L3_V"), 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", 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", "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", "LLI_I", "LLI_L", "LLIL_I", "LLIL_L", "LLII_I", "LLII_L",
@ -166,15 +166,18 @@ public final class GenerateJLIClassesPlugin implements Plugin {
); );
} }
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 // Map from DirectMethodHandle method type to internal ID, matching values
// of the corresponding constants in java.lang.invoke.MethodTypeForm // of the corresponding constants in java.lang.invoke.MethodTypeForm
private static final Map<String, Integer> DMH_METHOD_TYPE_MAP = private static final Map<String, Integer> DMH_METHOD_TYPE_MAP =
Map.of( Map.of(
DMH_INVOKE_VIRTUAL, 0, DMH_INVOKE_VIRTUAL, DMH_INVOKE_VIRTUAL_TYPE,
DMH_INVOKE_STATIC, 1, DMH_INVOKE_STATIC, 1,
DMH_INVOKE_SPECIAL, 2, DMH_INVOKE_SPECIAL, 2,
DMH_NEW_INVOKE_SPECIAL, 3, DMH_NEW_INVOKE_SPECIAL, 3,
DMH_INVOKE_INTERFACE, 4, DMH_INVOKE_INTERFACE, DMH_INVOKE_INTERFACE_TYPE,
DMH_INVOKE_STATIC_INIT, 5, DMH_INVOKE_STATIC_INIT, 5,
DMH_INVOKE_SPECIAL_IFC, 20 DMH_INVOKE_SPECIAL_IFC, 20
); );
@ -380,10 +383,23 @@ public final class GenerateJLIClassesPlugin implements Plugin {
if (mt.parameterCount() < 1 || if (mt.parameterCount() < 1 ||
mt.parameterType(0) != Object.class) { mt.parameterType(0) != Object.class) {
throw new PluginException( 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); 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); 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++; index++;
} }
} }

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -21,6 +21,7 @@
* questions. * questions.
*/ */
import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@ -61,7 +62,6 @@ public class GenerateJLIClassesPluginTest {
helper.generateDefaultModules(); helper.generateDefaultModules();
// Test that generate-jli is enabled by default // Test that generate-jli is enabled by default
Result result = JImageGenerator.getJLinkTask() Result result = JImageGenerator.getJLinkTask()
.modulePath(helper.defaultModulePath()) .modulePath(helper.defaultModulePath())
@ -71,10 +71,9 @@ public class GenerateJLIClassesPluginTest {
Path image = result.assertSuccess(); Path image = result.assertSuccess();
JImageValidator.validate( JImageValidator.validate(image.resolve("lib").resolve("modules"),
image.resolve("lib").resolve("modules"), classFilesForSpecies(GenerateJLIClassesPlugin.defaultSpecies()),
classFilesForSpecies(GenerateJLIClassesPlugin.defaultSpecies()), List.of());
List.of());
// Check that --generate-jli-classes=@file works as intended // Check that --generate-jli-classes=@file works as intended
Path baseFile = Files.createTempFile("base", "trace"); Path baseFile = Files.createTempFile("base", "trace");
@ -90,10 +89,32 @@ public class GenerateJLIClassesPluginTest {
image = result.assertSuccess(); image = result.assertSuccess();
JImageValidator.validate( JImageValidator.validate(image.resolve("lib").resolve("modules"),
image.resolve("lib").resolve("modules"), classFilesForSpecies(List.of(species)), // species should be in the image
classFilesForSpecies(List.of(species)), // species should be in the image classFilesForSpecies(List.of(species.substring(1)))); // but not it's immediate parent
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) { private static List<String> classFilesForSpecies(Collection<String> species) {