mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-14 18:03:44 +00:00
8366926: Unexpected exception occurs when executing code in a "local" JShell environment
Reviewed-by: liach, jlahoda
This commit is contained in:
parent
2e99ed6422
commit
d316d3f74f
@ -24,6 +24,7 @@
|
||||
*/
|
||||
package jdk.jshell.execution;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.reflect.Field;
|
||||
@ -34,6 +35,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Stream;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.ClassHierarchyResolver;
|
||||
import java.lang.classfile.ClassTransform;
|
||||
import java.lang.classfile.CodeBuilder;
|
||||
import java.lang.classfile.CodeElement;
|
||||
@ -85,9 +87,7 @@ public class LocalExecutionControl extends DirectExecutionControl {
|
||||
@Override
|
||||
public void load(ClassBytecodes[] cbcs)
|
||||
throws ClassInstallException, NotImplementedException, EngineTerminationException {
|
||||
super.load(Stream.of(cbcs)
|
||||
.map(cbc -> new ClassBytecodes(cbc.name(), instrument(cbc.bytecodes())))
|
||||
.toArray(ClassBytecodes[]::new));
|
||||
super.load(instrument(cbcs));
|
||||
}
|
||||
|
||||
private static final String CANCEL_CLASS = "REPL.$Cancel$";
|
||||
@ -95,8 +95,26 @@ public class LocalExecutionControl extends DirectExecutionControl {
|
||||
private static final String STOP_CHECK = "stopCheck";
|
||||
private static final ClassDesc CD_ThreadDeath = ClassDesc.of("java.lang.ThreadDeath");
|
||||
|
||||
private static byte[] instrument(byte[] classFile) {
|
||||
var cc = ClassFile.of();
|
||||
private static ClassBytecodes[] instrument(ClassBytecodes[] cbcs) {
|
||||
var cc = ClassFile.of(ClassFile.ClassHierarchyResolverOption.of(
|
||||
ClassHierarchyResolver.defaultResolver().orElse(
|
||||
ClassHierarchyResolver.ofResourceParsing(cd -> {
|
||||
String cName = cd.descriptorString();
|
||||
cName = cName.substring(1, cName.length() - 1).replace('/', '.');
|
||||
for (ClassBytecodes cbc : cbcs) {
|
||||
if (cName.equals(cbc.name())) {
|
||||
return new ByteArrayInputStream(cbc.bytecodes());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}))));
|
||||
|
||||
return Stream.of(cbcs)
|
||||
.map(cbc -> new ClassBytecodes(cbc.name(), instrument(cc, cbc.bytecodes())))
|
||||
.toArray(ClassBytecodes[]::new);
|
||||
}
|
||||
|
||||
private static byte[] instrument(ClassFile cc, byte[] classFile) {
|
||||
return cc.transformClass(cc.parse(classFile),
|
||||
ClassTransform.transformingMethodBodies(
|
||||
CodeTransform.ofStateful(StopCheckWeaver::new)));
|
||||
|
||||
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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 8366926
|
||||
* @summary Verify the instrumenation class hierarchy resolution 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 KullaTesting
|
||||
* @run junit/othervm LocalExecutionInstrumentationCHRTest
|
||||
*/
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
public class LocalExecutionInstrumentationCHRTest extends ReplToolTesting {
|
||||
|
||||
@Test
|
||||
public void verifyMyClassFoundOnClassPath() {
|
||||
test(new String[] { "--execution", "local" },
|
||||
a -> assertCommand(a, "public interface TestInterface {}", "| created interface TestInterface"),
|
||||
a -> assertCommand(a,
|
||||
"public class TestClass {"
|
||||
+ "public TestInterface foo(boolean b) {"
|
||||
+ "TestInterface test; "
|
||||
+ "if (b) {"
|
||||
+ "test = new TestInterfaceImpl1();"
|
||||
+ "} else {"
|
||||
+ "test = new TestInterfaceImpl2();"
|
||||
+ "}"
|
||||
+ "return test;"
|
||||
+ "}"
|
||||
+ "private class TestInterfaceImpl1 implements TestInterface {}"
|
||||
+ "private class TestInterfaceImpl2 implements TestInterface {}"
|
||||
+ "}", "| created class TestClass"),
|
||||
a -> assertCommand(a, "new TestClass().foo(true).getClass();", "$3 ==> class TestClass$TestInterfaceImpl1"),
|
||||
a -> assertCommand(a, "new TestClass().foo(false).getClass();", "$4 ==> class TestClass$TestInterfaceImpl2")
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user