8145964: NoClassDefFound error in transforming lambdas
Skip VM anonymous classes in retransformation and give an error for redefinition. Reviewed-by: dholmes, dcubed, never
This commit is contained in:
parent
5868fdc4b7
commit
85381e59e5
@ -7152,15 +7152,19 @@ class C2 extends C1 implements I2 {
|
||||
returns <code>JNI_FALSE</code>) the class can be neither
|
||||
redefined nor retransformed.
|
||||
<p/>
|
||||
Primitive classes (for example, <code>java.lang.Integer.TYPE</code>)
|
||||
and array classes are never modifiable.
|
||||
Primitive classes (for example, <code>java.lang.Integer.TYPE</code>),
|
||||
array classes, and some implementation defined classes are never modifiable.
|
||||
<p/>
|
||||
</description>
|
||||
<origin>new</origin>
|
||||
<capabilities>
|
||||
<capability id="can_redefine_any_class">
|
||||
If possessed then all classes (except primitive and array classes)
|
||||
are modifiable.
|
||||
If possessed then all classes (except primitive, array, and some implementation defined
|
||||
classes) are modifiable (redefine or retransform).
|
||||
</capability>
|
||||
<capability id="can_retransform_any_class">
|
||||
If possessed then all classes (except primitive, array, and some implementation defined
|
||||
classes) are modifiable with <functionlink id="RetransformClasses"/>.
|
||||
</capability>
|
||||
<capability id="can_redefine_classes">
|
||||
No effect on the result of the function.
|
||||
@ -9900,7 +9904,7 @@ myInit() {
|
||||
</capabilityfield>
|
||||
<capabilityfield id="can_redefine_any_class">
|
||||
<description>
|
||||
Can modify (retransform or redefine) any non-primitive non-array class.
|
||||
Can modify (retransform or redefine) any modifiable class.
|
||||
See <functionlink id="IsModifiableClass"/>.
|
||||
</description>
|
||||
</capabilityfield>
|
||||
@ -10024,7 +10028,8 @@ myInit() {
|
||||
</capabilityfield>
|
||||
<capabilityfield id="can_retransform_any_class" since="1.1">
|
||||
<description>
|
||||
<functionlink id="RetransformClasses"/> can be called on any class
|
||||
<functionlink id="RetransformClasses"/> can be called on any modifiable class.
|
||||
See <functionlink id="IsModifiableClass"/>.
|
||||
(<fieldlink id="can_retransform_classes" struct="jvmtiCapabilities"/>
|
||||
must also be set)
|
||||
</description>
|
||||
@ -12494,8 +12499,8 @@ myInit() {
|
||||
Otherwise, this event may be sent before the VM is initialized (the start
|
||||
<functionlink id="GetPhase">phase</functionlink>).
|
||||
Some classes might not be compatible
|
||||
with the function (eg. ROMized classes) and this event will not be
|
||||
generated for these classes.
|
||||
with the function (eg. ROMized classes or implementation defined classes) and this event will
|
||||
not be generated for these classes.
|
||||
<p/>
|
||||
The agent must allocate the space for the modified
|
||||
class file data buffer
|
||||
@ -14498,6 +14503,10 @@ typedef void (JNICALL *jvmtiEventVMInit)
|
||||
- Add new capability can_generate_early_class_hook_events
|
||||
- Add new function GetNamedModule
|
||||
</change>
|
||||
<change date="16 August 2016" version="9.0.0">
|
||||
Clarified can_redefine_any_classes, can_retransform_any_classes and IsModifiableClass API to
|
||||
disallow some implementation defined classes.
|
||||
</change>
|
||||
</changehistory>
|
||||
|
||||
</specification>
|
||||
|
@ -283,7 +283,7 @@ JvmtiEnv::RetransformClasses(jint class_count, const jclass* classes) {
|
||||
return JVMTI_ERROR_INVALID_CLASS;
|
||||
}
|
||||
|
||||
if (java_lang_Class::is_primitive(k_mirror)) {
|
||||
if (!VM_RedefineClasses::is_modifiable_class(k_mirror)) {
|
||||
return JVMTI_ERROR_UNMODIFIABLE_CLASS;
|
||||
}
|
||||
|
||||
@ -294,9 +294,6 @@ JvmtiEnv::RetransformClasses(jint class_count, const jclass* classes) {
|
||||
if (status & (JVMTI_CLASS_STATUS_ERROR)) {
|
||||
return JVMTI_ERROR_INVALID_CLASS;
|
||||
}
|
||||
if (status & (JVMTI_CLASS_STATUS_ARRAY)) {
|
||||
return JVMTI_ERROR_UNMODIFIABLE_CLASS;
|
||||
}
|
||||
|
||||
instanceKlassHandle ikh(current_thread, k_oop);
|
||||
if (ikh->get_cached_class_file_bytes() == NULL) {
|
||||
|
@ -130,7 +130,7 @@ bool VM_RedefineClasses::doit_prologue() {
|
||||
}
|
||||
|
||||
oop mirror = JNIHandles::resolve_non_null(_class_defs[i].klass);
|
||||
// classes for primitives and arrays cannot be redefined
|
||||
// classes for primitives and arrays and vm anonymous classes cannot be redefined
|
||||
// check here so following code can assume these classes are InstanceKlass
|
||||
if (!is_modifiable_class(mirror)) {
|
||||
_res = JVMTI_ERROR_UNMODIFIABLE_CLASS;
|
||||
@ -250,9 +250,14 @@ bool VM_RedefineClasses::is_modifiable_class(oop klass_mirror) {
|
||||
if (java_lang_Class::is_primitive(klass_mirror)) {
|
||||
return false;
|
||||
}
|
||||
Klass* the_class_oop = java_lang_Class::as_Klass(klass_mirror);
|
||||
Klass* k = java_lang_Class::as_Klass(klass_mirror);
|
||||
// classes for arrays cannot be redefined
|
||||
if (the_class_oop == NULL || !the_class_oop->is_instance_klass()) {
|
||||
if (k == NULL || !k->is_instance_klass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cannot redefine or retransform an anonymous class.
|
||||
if (InstanceKlass::cast(k)->is_anonymous()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
171
hotspot/test/runtime/RedefineTests/ModifyAnonymous.java
Normal file
171
hotspot/test/runtime/RedefineTests/ModifyAnonymous.java
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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
|
||||
* @library /test/lib
|
||||
* @summary Test that retransforming and redefining anonymous classes gets UnmodifiableClassException
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @modules java.instrument
|
||||
* jdk.jartool/sun.tools.jar
|
||||
* @run main ModifyAnonymous buildagent
|
||||
* @run main/othervm -javaagent:redefineagent.jar ModifyAnonymous
|
||||
*/
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.NoSuchFieldException;
|
||||
import java.lang.NoSuchMethodException;
|
||||
import java.lang.RuntimeException;
|
||||
import java.lang.instrument.ClassDefinition;
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.security.ProtectionDomain;
|
||||
import jdk.test.lib.*;
|
||||
|
||||
public class ModifyAnonymous {
|
||||
|
||||
public static class LambdaTransformer implements ClassFileTransformer {
|
||||
@Override
|
||||
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
|
||||
ProtectionDomain protectionDomain, byte[] classfileBuffer)
|
||||
throws IllegalClassFormatException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Instrumentation inst = null;
|
||||
static volatile boolean done = false;
|
||||
|
||||
public static void premain(String args, Instrumentation instrumentation) {
|
||||
|
||||
inst = instrumentation;
|
||||
System.out.println("javaagent in da house!");
|
||||
instrumentation.addTransformer(new LambdaTransformer());
|
||||
}
|
||||
|
||||
private static void buildAgent() {
|
||||
try {
|
||||
ClassFileInstaller.main("ModifyAnonymous");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not write agent classfile", e);
|
||||
}
|
||||
|
||||
try {
|
||||
PrintWriter pw = new PrintWriter("MANIFEST.MF");
|
||||
pw.println("Premain-Class: ModifyAnonymous");
|
||||
pw.println("Agent-Class: ModifyAnonymous");
|
||||
pw.println("Can-Retransform-Classes: true");
|
||||
pw.println("Can-Redefine-Classes: true");
|
||||
pw.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException("Could not write manifest file for the agent", e);
|
||||
}
|
||||
|
||||
sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar");
|
||||
if (!jarTool.run(new String[] { "-cmf", "MANIFEST.MF", "redefineagent.jar", "ModifyAnonymous.class" })) {
|
||||
throw new RuntimeException("Could not write the agent jar file");
|
||||
}
|
||||
}
|
||||
|
||||
public static class InstanceMethodCallSiteApp {
|
||||
|
||||
public static void test() throws InterruptedException {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
InstanceMethodCallSiteApp app = new InstanceMethodCallSiteApp();
|
||||
Runnable r = app::doWork; // this creates an anonymous class
|
||||
while (!done) {
|
||||
r.run();
|
||||
Thread.sleep(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void doWork() {
|
||||
System.out.print(".");
|
||||
}
|
||||
}
|
||||
|
||||
static void runTest() {
|
||||
while (!done) {
|
||||
Class[] allLoadedClasses = inst.getAllLoadedClasses();
|
||||
for (Class clazz : allLoadedClasses) {
|
||||
final String name = clazz.getName();
|
||||
if (name.contains("$$Lambda$") && name.contains("App")) {
|
||||
if (inst.isModifiableClass(clazz)) {
|
||||
throw new RuntimeException ("Class should not be modifiable");
|
||||
}
|
||||
// Try to modify them anyway.
|
||||
try {
|
||||
System.out.println("retransform called for " + name);
|
||||
inst.retransformClasses(clazz);
|
||||
} catch(java.lang.instrument.UnmodifiableClassException t) {
|
||||
System.out.println("PASSED: expecting UnmodifiableClassException");
|
||||
t.printStackTrace();
|
||||
}
|
||||
try {
|
||||
System.out.println("redefine called for " + name);
|
||||
String newclass = "class Dummy {}";
|
||||
byte[] bytecode = InMemoryJavaCompiler.compile("Dummy", newclass);
|
||||
ClassDefinition cld = new ClassDefinition(clazz, bytecode);
|
||||
inst.redefineClasses(new ClassDefinition[] { cld });
|
||||
} catch(java.lang.instrument.UnmodifiableClassException t) {
|
||||
System.out.println("PASSED: expecting UnmodifiableClassException");
|
||||
t.printStackTrace();
|
||||
} catch(java.lang.ClassNotFoundException e) {
|
||||
throw new RuntimeException ("ClassNotFoundException thrown");
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String argv[]) throws InterruptedException, RuntimeException {
|
||||
if (argv.length == 1 && argv[0].equals("buildagent")) {
|
||||
buildAgent();
|
||||
return;
|
||||
}
|
||||
|
||||
if (inst == null) {
|
||||
throw new RuntimeException("Instrumentation object was null");
|
||||
}
|
||||
|
||||
new Thread() {
|
||||
public void run() {
|
||||
runTest();
|
||||
}
|
||||
}.start();
|
||||
|
||||
// Test that NCDFE is not thrown for anonymous class:
|
||||
// ModifyAnonymous$InstanceMethodCallSiteApp$$Lambda$18
|
||||
try {
|
||||
ModifyAnonymous test = new ModifyAnonymous();
|
||||
InstanceMethodCallSiteApp.test();
|
||||
} catch (NoClassDefFoundError e) {
|
||||
throw new RuntimeException("FAILED: NoClassDefFoundError thrown for " + e.getMessage());
|
||||
}
|
||||
System.out.println("PASSED: NoClassDefFound error not thrown");
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user