8314327: Issues with JShell when using "local" execution engine

Reviewed-by: jlahoda
This commit is contained in:
Archie Cobbs 2023-12-05 17:57:43 +00:00 committed by Vicente Romero
parent db5613af89
commit 640d7f31b2
6 changed files with 316 additions and 2 deletions

@ -73,6 +73,10 @@ class DefaultLoaderDelegate implements LoaderDelegate {
super(new URL[0]);
}
RemoteClassLoader(ClassLoader parent) {
super(new URL[0], parent);
}
private class ResourceURLStreamHandler extends URLStreamHandler {
private final String name;
@ -216,11 +220,31 @@ class DefaultLoaderDelegate implements LoaderDelegate {
}
}
/**
* Default constructor.
*
* <p>
* The internal class loader will use the
* {@linkplain ClassLoader#getSystemClassLoader system class loader} as its parent loader.
*/
public DefaultLoaderDelegate() {
this.loader = new RemoteClassLoader();
Thread.currentThread().setContextClassLoader(loader);
}
/**
* Creates an instance with the given parent class loader.
*
* <p>
* The internal class loader will use the given parent class loader.
*
* @param parent parent class loader
*/
public DefaultLoaderDelegate(ClassLoader parent) {
this.loader = new RemoteClassLoader(parent);
Thread.currentThread().setContextClassLoader(loader);
}
@Override
public void load(ClassBytecodes[] cbcs)
throws ClassInstallException, EngineTerminationException {

@ -60,11 +60,21 @@ public class LocalExecutionControl extends DirectExecutionControl {
}
/**
* Create an instance using the default class loading.
* Create an instance using the default class loading, which delegates to the system class loader.
*/
public LocalExecutionControl() {
}
/**
* Create an instance using the default class loading, but delegating to the specified parent class loader.
*
* @param parent parent class loader
* @since 22
*/
public LocalExecutionControl(ClassLoader parent) {
super(new DefaultLoaderDelegate(parent));
}
@Override
public void load(ClassBytecodes[] cbcs)
throws ClassInstallException, NotImplementedException, EngineTerminationException {

@ -25,6 +25,7 @@
package jdk.jshell.execution;
import java.util.List;
import java.util.Map;
import jdk.jshell.spi.ExecutionControl;
import jdk.jshell.spi.ExecutionControlProvider;
@ -78,7 +79,37 @@ public class LocalExecutionControlProvider implements ExecutionControlProvider{
*/
@Override
public ExecutionControl generate(ExecutionEnv env, Map<String, String> parameters) {
return new LocalExecutionControl();
// Create ExecutionControl
ExecutionControl executionControl = createExecutionControl(env, parameters);
// Apply any configured class path
List<String> remoteOptions = env.extraRemoteVMOptions();
int classPathIndex = remoteOptions.indexOf("--class-path") + 1;
if (classPathIndex > 0 && classPathIndex < remoteOptions.size()) {
try {
executionControl.addToClasspath(remoteOptions.get(classPathIndex));
} catch (ExecutionControl.ExecutionControlException e) {
throw new RuntimeException("error configuring class path", e);
}
}
// Done
return executionControl;
}
/**
* Create a new {@link ExecutionControl} instance.
*
* <p>
* This method is invoked by {@link #generate generate()}.
*
* @param env the {@code ExecutionEnv} for which the {@link ExecutionControl} should be created
* @param parameters the parameters that were passed to {@link #generate generate()}
* @return the newly created {@code ExecutionControl}
* @since 22
*/
public ExecutionControl createExecutionControl(ExecutionEnv env, Map<String, String> parameters) {
return new LocalExecutionControl(new DefaultLoaderDelegate());
}
}

@ -0,0 +1,59 @@
/*
* Copyright (c) 2023, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8314327
* @summary Verify the "--class-path" flag works properly in local execution mode
* @library /tools/lib
* @modules
* jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* @build toolbox.ToolBox toolbox.JavacTask LocalExecutionTestSupport
* @run testng/othervm LocalExecutionClassPathTest
*/
import java.util.Locale;
import org.testng.annotations.Test;
public class LocalExecutionClassPathTest extends LocalExecutionTestSupport {
@Override
public void test(Locale locale, boolean defaultStartUp, String[] args, String startMsg, ReplTest... tests) {
// Set local execution with context class loader
args = this.prependArgs(args,
"--execution", "local",
"--class-path", this.classesDir.toString());
// Verify MyClass can be found by both the compiler and the execution engine
super.test(locale, defaultStartUp, args, startMsg, tests);
}
@Test
public void verifyMyClassFoundOnClassPath() {
test(new String[] { "--no-startup" },
a -> assertCommand(a, "test.MyClass.class", "$1 ==> class test.MyClass")
);
}
}

@ -0,0 +1,104 @@
/*
* Copyright (c) 2023, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8314327
* @summary Verify function of LocalExecutionControlProvider createExecutionControl() method override
* @library /tools/lib
* @modules
* jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* @build toolbox.ToolBox toolbox.JavacTask LocalExecutionTestSupport
* @run testng/othervm LocalExecutionContextLoaderParentTest
*/
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.nio.file.Files;
import jdk.jshell.execution.LocalExecutionControl;
import jdk.jshell.execution.LocalExecutionControlProvider;
import jdk.jshell.spi.ExecutionControl;
import jdk.jshell.spi.ExecutionControlProvider;
import jdk.jshell.spi.ExecutionEnv;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeTest;
public class LocalExecutionContextLoaderParentTest extends LocalExecutionTestSupport {
@BeforeTest
public void installParentTestProvider() throws IOException {
Path dir = createSubdir(classesDir, "META-INF/services");
Files.write(dir.resolve(ExecutionControlProvider.class.getName()),
Arrays.asList(ParentTestExecutionControlProvider.class.getName()));
}
@Override
public void test(Locale locale, boolean defaultStartUp, String[] args, String startMsg, ReplTest... tests) {
// Make test.MyClass visible to the context class loader
final URL classesDirURL;
try {
classesDirURL = this.classesDir.toUri().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
Thread.currentThread().setContextClassLoader(new URLClassLoader(new URL[] { classesDirURL }));
// Set local execution with context class loader as parent loader
args = this.prependArgs(args, "--execution", "parentTest");
// Verify the execution engine can find MyClass (we don't care whether the compiler can find it in this test)
super.test(locale, defaultStartUp, args, startMsg, tests);
}
@Test
public void verifyMyClassFoundByExecutionEngine() {
test(new String[] { "--no-startup" },
a -> assertCommand(a, "Class.forName(\"test.MyClass\").getField(\"FOO\").get(null)", "$1 ==> \"bar\"")
);
}
// ParentTestExecutionControlProvider
public static class ParentTestExecutionControlProvider extends LocalExecutionControlProvider {
@Override
public String name() {
return "parentTest";
}
@Override
public ExecutionControl createExecutionControl(ExecutionEnv env, Map<String, String> parameters) {
return new LocalExecutionControl(Thread.currentThread().getContextClassLoader());
}
}
}

@ -0,0 +1,86 @@
/*
* Copyright (c) 2023, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.testng.annotations.BeforeTest;
import toolbox.JavacTask;
import toolbox.TestRunner;
import toolbox.ToolBox;
/*
* This class installs a class in a temporary diretory so we can test
* finding classes that are not visible to the system class loader.
*/
public class LocalExecutionTestSupport extends ReplToolTesting {
public static final String MY_CLASS_SOURCE = """
package test;
public class MyClass {
public static final String FOO = "bar";
}""";
protected final ToolBox tb = new ToolBox();
protected Path baseDir; // base working directory
protected Path sourcesDir; // sources directory
protected Path classesDir; // classes directory
// Install file "test/MyClass.class" in some temporary directory somewhere
@BeforeTest
public void installMyClass() throws IOException {
// Create directories
baseDir = Files.createTempDirectory(getClass().getSimpleName()).toAbsolutePath();
sourcesDir = createWorkSubdir("sources");
classesDir = createWorkSubdir("classes");
// Create source file
tb.writeJavaFiles(sourcesDir, MY_CLASS_SOURCE);
// Compile source file
new JavacTask(tb)
.outdir(classesDir)
.files(sourcesDir.resolve("test/MyClass.java"))
.run();
}
protected Path createWorkSubdir(String name) throws IOException {
return createSubdir(baseDir, name);
}
protected Path createSubdir(Path base, String name) throws IOException {
Path dir = base.resolve(name);
Files.createDirectories(dir);
return dir;
}
protected String[] prependArgs(String[] args, String... prepends) {
String[] newArgs = new String[prepends.length + args.length];
System.arraycopy(prepends, 0, newArgs, 0, prepends.length);
System.arraycopy(args, 0, newArgs, prepends.length, args.length);
return newArgs;
}
}