jdk-24/test/hotspot/jtreg/vmTestbase/vm/runtime/defmeth/RedefineTest.java

236 lines
8.4 KiB
Java
Raw Normal View History

/*
* Copyright (c) 2013, 2018, 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.
*/
package vm.runtime.defmeth;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import nsk.share.Pair;
import nsk.share.TestFailure;
import nsk.share.test.TestBase;
import vm.runtime.defmeth.shared.DefMethTest;
import vm.runtime.defmeth.shared.DefMethTestFailure;
import vm.runtime.defmeth.shared.MemoryClassLoader;
import vm.runtime.defmeth.shared.annotation.NotApplicableFor;
import vm.runtime.defmeth.shared.builder.TestBuilder;
import vm.runtime.defmeth.shared.executor.TestExecutor;
import vm.runtime.defmeth.shared.data.Clazz;
import vm.runtime.defmeth.shared.data.ConcreteClass;
import vm.runtime.defmeth.shared.data.Interface;
import vm.runtime.defmeth.shared.data.Tester;
import static vm.runtime.defmeth.shared.ExecutionMode.*;
/*
* Basic scenarios on class redefinition.
*/
public class RedefineTest extends DefMethTest {
public static void main(String[] args) {
TestBase.runTest(new RedefineTest(), args);
}
@Override
protected void configure() {
// There are no testers being generated for reflection-based scenarios,
// so scenarios on class redefinition don't work
String mode = factory.getExecutionMode();
if ( "REFLECTION".equals(mode) || "INVOKE_WITH_ARGS".equals(mode)) {
throw new TestFailure("RedefineTest isn't applicable to reflection-based execution scenario " +
"(REDEFINE & INVOKE_WITH_ARGS).");
}
}
/**
* Run test {@code b1} w/ redefined {@code classes} from {@code b2}.
*
* @param b1
* @param b2
* @param classes
*/
private void redefineAndRun(TestBuilder b1, TestBuilder b2, Clazz... classes) {
TestExecutor executor = b1.prepare();
getLog().info("Before");
List<Pair<Tester,Throwable>> errorsBefore =
executor.run(); // run b1
// redefine in b1
MemoryClassLoader cl = executor.getLoader(); // b1.cl
Map<String,byte[]> cf = b2.produce(); //
Map<String,byte[]> forRedef = new HashMap<>();
for (Clazz clz : classes) {
String name = clz.name();
forRedef.put(name, cf.get(name));
}
cl.modifyClasses(forRedef, factory.isRetransformClasses());
getLog().info("After");
List<Pair<Tester,Throwable>> errorsAfter =
executor.run();
if (!errorsBefore.isEmpty()) {
throw new DefMethTestFailure(errorsBefore);
}
if (!errorsAfter.isEmpty()) {
throw new DefMethTestFailure(errorsAfter);
}
}
/*
* Before redefinition:
* interface I { public int m() { return 1; } }
* class C extends I { public int m() { return 2; } }
*
* TEST: I i = new C(); i.m() == 2
* TEST: C c = new C(); c.m() == 2
*
* After redefinition:
* interface I { public int m() { return 1; } }
* class C extends I { public int m() { return 3; } }
*
* TEST: I i = new C(); i.m() == 3
* TEST: C c = new C(); c.m() == 3
*/
@NotApplicableFor(modes = { REFLECTION, INVOKE_WITH_ARGS }) // reflection-based scenarios rely on checks in bytecode
public void testRedefineConcreteMethod() {
TestBuilder before = factory.getBuilder();
{ // Before redefinition
Interface I = before.intf("I")
.defaultMethod("m", "()I").returns(1).build()
.build();
ConcreteClass C = before.clazz("C").implement(I)
.concreteMethod("m", "()I").returns(2).build()
.build();
before.test().callSite(I, C, "m", "()I").returns(2).done()
.test().callSite(C, C, "m", "()I").returns(2).done();
}
{ // After redefinition
TestBuilder after = factory.getBuilder();
Interface I = after.intf("I")
.defaultMethod("m", "()I").returns(1).build()
.build();
ConcreteClass C = after.clazz("C").implement(I)
.concreteMethod("m", "()I").returns(3).build()
.build();
Tester T1 = after.test().callSite(I, C, "m", "()I").returns(3).build();
Tester T2 = after.test().callSite(C, C, "m", "()I").returns(3).build();
redefineAndRun(before, after, C, T1, T2);
}
}
/*
* Before redefinition:
* interface I { public int m() { return 1; } }
* class C extends I { public int m() { return 2; } }
*
* TEST: I i = new C(); i.m() == 2
* TEST: C c = new C(); c.m() == 2
*
* After redefinition:
* interface I { public int m() { return 3; } }
* class C extends I { public int m() { return 2; } }
*
* TEST: I i = new C(); i.m() == 2
* TEST: C c = new C(); c.m() == 2
*/
@NotApplicableFor(modes = { REFLECTION, INVOKE_WITH_ARGS }) // reflection-based scenarios rely on checks in bytecode
public void testRedefineDefaultMethod() {
TestBuilder before = factory.getBuilder();
{ // Before redefinition
Interface I = before.intf("I")
.defaultMethod("m", "()I").returns(1).build()
.build();
ConcreteClass C = before.clazz("C").implement(I)
.concreteMethod("m", "()I").returns(2).build()
.build();
before.test().callSite(I, C, "m", "()I").returns(2).done()
.test().callSite(C, C, "m", "()I").returns(2).done();
}
{ // After redefinition
TestBuilder after = factory.getBuilder();
Interface I = after.intf("I")
.defaultMethod("m", "()I").returns(3).build()
.build();
ConcreteClass C = after.clazz("C").implement(I)
.concreteMethod("m", "()I").returns(2).build()
.build();
redefineAndRun(before, after, C);
}
}
/*
* Before redefinition:
* interface I { public int m() { return 1; } }
* class C extends I {}
*
* TEST: I i = new C(); i.m() == 1
* TEST: C c = new C(); c.m() == 1
*
* After redefinition:
* interface I { public int m() { return 2; } }
* class C extends I {}
*
* TEST: I i = new C(); i.m() == 2
* TEST: C c = new C(); c.m() == 2
*/
@NotApplicableFor(modes = { REFLECTION, INVOKE_WITH_ARGS }) // reflection-based scenarios rely on checks in bytecode
public void testRedefineDefMethInConcreteClass() {
TestBuilder before = factory.getBuilder();
{ // Before redefinition
Interface I = before.intf("I")
.defaultMethod("m", "()I").returns(1).build()
.build();
ConcreteClass C = before.clazz("C").implement(I).build();
before.test().callSite(I, C, "m", "()I").returns(1).done()
.test().callSite(C, C, "m", "()I").returns(1).done();
}
{ // After redefinition
TestBuilder after = factory.getBuilder();
Interface I = after.intf("I")
.defaultMethod("m", "()I").returns(2).build()
.build();
ConcreteClass C = after.clazz("C").implement(I).build();
Tester T1 = after.test().callSite(I, C, "m", "()I").returns(2).build();
Tester T2 = after.test().callSite(C, C, "m", "()I").returns(2).build();
redefineAndRun(before, after, I, T1, T2);
}
}
}