# HG changeset patch # User coleenp # Date 1471639345 -7200 # Node ID 543c4fcd4ae299875396428f8b64d27db5adfb2d # Parent 99b0dc9d0b036f05bda8c2215019963d136c8139# Parent 22fa174b2af88935172a3a29321d97f2a89c59ad Merge diff -r 99b0dc9d0b03 -r 543c4fcd4ae2 hotspot/src/share/vm/prims/jvmti.xml --- a/hotspot/src/share/vm/prims/jvmti.xml Fri Aug 19 20:38:30 2016 +0200 +++ b/hotspot/src/share/vm/prims/jvmti.xml Fri Aug 19 22:42:25 2016 +0200 @@ -7152,15 +7152,19 @@ returns JNI_FALSE) the class can be neither redefined nor retransformed.

- Primitive classes (for example, java.lang.Integer.TYPE) - and array classes are never modifiable. + Primitive classes (for example, java.lang.Integer.TYPE), + array classes, and some implementation defined classes are never modifiable.

new - 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). + + + If possessed then all classes (except primitive, array, and some implementation defined + classes) are modifiable with . No effect on the result of the function. @@ -9900,7 +9904,7 @@ - Can modify (retransform or redefine) any non-primitive non-array class. + Can modify (retransform or redefine) any modifiable class. See . @@ -10024,7 +10028,8 @@ - can be called on any class + can be called on any modifiable class. + See . ( must also be set) @@ -12494,8 +12499,8 @@ Otherwise, this event may be sent before the VM is initialized (the start phase). 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.

The agent must allocate the space for the modified class file data buffer @@ -14498,6 +14503,10 @@ - Add new capability can_generate_early_class_hook_events - Add new function GetNamedModule + + Clarified can_redefine_any_classes, can_retransform_any_classes and IsModifiableClass API to + disallow some implementation defined classes. + diff -r 99b0dc9d0b03 -r 543c4fcd4ae2 hotspot/src/share/vm/prims/jvmtiEnv.cpp --- a/hotspot/src/share/vm/prims/jvmtiEnv.cpp Fri Aug 19 20:38:30 2016 +0200 +++ b/hotspot/src/share/vm/prims/jvmtiEnv.cpp Fri Aug 19 22:42:25 2016 +0200 @@ -283,7 +283,7 @@ 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 @@ 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) { diff -r 99b0dc9d0b03 -r 543c4fcd4ae2 hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp --- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp Fri Aug 19 20:38:30 2016 +0200 +++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp Fri Aug 19 22:42:25 2016 +0200 @@ -130,7 +130,7 @@ } 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 @@ 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; diff -r 99b0dc9d0b03 -r 543c4fcd4ae2 hotspot/test/runtime/RedefineTests/ModifyAnonymous.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/RedefineTests/ModifyAnonymous.java Fri Aug 19 22:42:25 2016 +0200 @@ -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"); + } +}