--- old/make/hotspot/symbols/symbols-unix 2019-09-04 11:45:58.000000000 -0700 +++ new/make/hotspot/symbols/symbols-unix 2019-09-04 11:45:58.000000000 -0700 @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2019, 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 @@ -149,6 +149,7 @@ JVM_IsThreadAlive JVM_IsVMGeneratedMethodIx JVM_LatestUserDefinedLoader +JVM_LinkClass JVM_LoadLibrary JVM_MaxMemory JVM_MaxObjectInspectionAge --- old/src/hotspot/share/include/jvm.h 2019-09-04 11:45:59.000000000 -0700 +++ new/src/hotspot/share/include/jvm.h 2019-09-04 11:45:59.000000000 -0700 @@ -345,6 +345,11 @@ JNIEXPORT jclass JNICALL JVM_FindPrimitiveClass(JNIEnv *env, const char *utf); +/* + * Link the 'arg' class (unless ClassForNameDeferLinking is set) + */ +JNIEXPORT void JNICALL +JVM_LinkClass(JNIEnv *env, jclass classClass, jclass arg); /* * Find a class from a boot class loader. Returns NULL if class not found. --- old/src/hotspot/share/prims/jni.cpp 2019-09-04 11:46:00.000000000 -0700 +++ new/src/hotspot/share/prims/jni.cpp 2019-09-04 11:46:00.000000000 -0700 @@ -417,7 +417,7 @@ } TempNewSymbol sym = SymbolTable::new_symbol(name); - result = find_class_from_class_loader(env, sym, true, loader, + result = find_class_from_class_loader(env, sym, true, true, loader, protection_domain, true, thread); if (log_is_enabled(Debug, class, resolve) && result != NULL) { @@ -3289,7 +3289,7 @@ Handle protection_domain; // null protection domain TempNewSymbol sym = SymbolTable::new_symbol(name); - jclass result = find_class_from_class_loader(env, sym, true, loader, protection_domain, true, CHECK_NULL); + jclass result = find_class_from_class_loader(env, sym, true, true, loader, protection_domain, true, CHECK_NULL); if (log_is_enabled(Debug, class, resolve) && result != NULL) { trace_class_resolution(java_lang_Class::as_Klass(JNIHandles::resolve_non_null(result))); --- old/src/hotspot/share/prims/jvm.cpp 2019-09-04 11:46:01.000000000 -0700 +++ new/src/hotspot/share/prims/jvm.cpp 2019-09-04 11:46:01.000000000 -0700 @@ -717,6 +717,17 @@ // Misc. class handling /////////////////////////////////////////////////////////// +JVM_ENTRY(void, JVM_LinkClass(JNIEnv* env, jclass classClass, jclass arg)) + JVMWrapper("JVM_LinkClass"); + + oop r = JNIHandles::resolve(arg); + Klass* klass = java_lang_Class::as_Klass(r); + + if (!ClassForNameDeferLinking && klass->is_instance_klass()) { + InstanceKlass::cast(klass)->link_class(CHECK); + } +JVM_END + JVM_ENTRY(jclass, JVM_GetCallerClass(JNIEnv* env)) JVMWrapper("JVM_GetCallerClass"); @@ -827,9 +838,10 @@ Handle h_loader(THREAD, loader_oop); Handle h_prot(THREAD, protection_domain); - jclass result = find_class_from_class_loader(env, h_name, init, h_loader, - h_prot, false, THREAD); + jboolean link = !ClassForNameDeferLinking; + jclass result = find_class_from_class_loader(env, h_name, init, link, h_loader, + h_prot, false, THREAD); if (log_is_enabled(Debug, class, resolve) && result != NULL) { trace_class_resolution(java_lang_Class::as_Klass(JNIHandles::resolve_non_null(result))); } @@ -866,7 +878,7 @@ } Handle h_loader(THREAD, class_loader); Handle h_prot (THREAD, protection_domain); - jclass result = find_class_from_class_loader(env, h_name, init, h_loader, + jclass result = find_class_from_class_loader(env, h_name, init, false, h_loader, h_prot, true, thread); if (log_is_enabled(Debug, class, resolve) && result != NULL) { @@ -3424,7 +3436,7 @@ // Shared JNI/JVM entry points ////////////////////////////////////////////////////////////// -jclass find_class_from_class_loader(JNIEnv* env, Symbol* name, jboolean init, +jclass find_class_from_class_loader(JNIEnv* env, Symbol* name, jboolean init, jboolean link, Handle loader, Handle protection_domain, jboolean throwError, TRAPS) { // Security Note: @@ -3435,9 +3447,11 @@ // if there is no security manager in 3-arg Class.forName(). Klass* klass = SystemDictionary::resolve_or_fail(name, loader, protection_domain, throwError != 0, CHECK_NULL); - // Check if we should initialize the class + // Check if we should initialize the class (which implies linking), or just link it if (init && klass->is_instance_klass()) { klass->initialize(CHECK_NULL); + } else if (link && klass->is_instance_klass()) { + InstanceKlass::cast(klass)->link_class(CHECK_NULL); } return (jclass) JNIHandles::make_local(env, klass->java_mirror()); } --- old/src/hotspot/share/prims/jvm_misc.hpp 2019-09-04 11:46:03.000000000 -0700 +++ new/src/hotspot/share/prims/jvm_misc.hpp 2019-09-04 11:46:02.000000000 -0700 @@ -31,7 +31,8 @@ // Useful entry points shared by JNI and JVM interface. // We do not allow real JNI or JVM entry point to call each other. -jclass find_class_from_class_loader(JNIEnv* env, Symbol* name, jboolean init, Handle loader, Handle protection_domain, jboolean throwError, TRAPS); +jclass find_class_from_class_loader(JNIEnv* env, Symbol* name, jboolean init, jboolean link, + Handle loader, Handle protection_domain, jboolean throwError, TRAPS); void trace_class_resolution(Klass* to_class); --- old/src/hotspot/share/runtime/globals.hpp 2019-09-04 11:46:04.000000000 -0700 +++ new/src/hotspot/share/runtime/globals.hpp 2019-09-04 11:46:03.000000000 -0700 @@ -2164,6 +2164,9 @@ "Maximum total size of NIO direct-buffer allocations") \ range(0, max_jlong) \ \ + product(bool, ClassForNameDeferLinking, false, \ + "Revert to not linking in Class.forName()") \ + \ /* Flags used for temporary code during development */ \ \ diagnostic(bool, UseNewCode, false, \ --- old/src/java.base/share/classes/java/lang/Class.java 2019-09-04 11:46:05.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/Class.java 2019-09-04 11:46:04.000000000 -0700 @@ -488,13 +488,21 @@ cl = module.getClassLoader(); } + Class ret; if (cl != null) { - return cl.loadClass(module, name); + ret = cl.loadClass(module, name); } else { - return BootLoader.loadClass(module, name); + ret = BootLoader.loadClass(module, name); } + if (ret != null) { + // The loaded class should also be linked + linkClass(ret); + } + return ret; } + private static native void linkClass(Class c); + /** * Creates a new instance of the class represented by this {@code Class} * object. The class is instantiated as if by a {@code new} --- old/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2019-09-04 11:46:06.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2019-09-04 11:46:05.000000000 -0700 @@ -1927,12 +1927,17 @@ } /** - * Looks up a class by name from the lookup context defined by this {@code Lookup} object. The static + * Looks up a class by name from the lookup context defined by this {@code Lookup} object. + * This method attempts to locate, load, and link the class. The static * initializer of the class is not run. *

* The lookup context here is determined by the {@linkplain #lookupClass() lookup class}, its class * loader, and the {@linkplain #lookupModes() lookup modes}. In particular, the method first attempts to * load the requested class, and then determines whether the class is accessible to this lookup object. + *

+ * Note that this method throws errors related to loading and linking as + * specified in Sections 12.2 and 12.3 of The Java Language + * Specification. * * @param targetName the fully qualified name of the class to be looked up. * @return the requested class. --- old/src/java.base/share/classes/sun/launcher/LauncherHelper.java 2019-09-04 11:46:07.000000000 -0700 +++ new/src/java.base/share/classes/sun/launcher/LauncherHelper.java 2019-09-04 11:46:07.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2019, 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 @@ -62,6 +62,7 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; +import java.security.AccessControlException; import java.text.Normalizer; import java.text.MessageFormat; import java.util.ArrayList; @@ -724,7 +725,10 @@ } catch (LinkageError le) { abort(null, "java.launcher.module.error3", mainClass, m.getName(), le.getClass().getName() + ": " + le.getLocalizedMessage()); - } + } catch (AccessControlException ace) { + abort(ace, "java.launcher.module.error5", mainClass, m.getName(), + ace.getClass().getName(), ace.getLocalizedMessage()); + } if (c == null) { abort(null, "java.launcher.module.error2", mainClass, mainModule); } @@ -780,6 +784,9 @@ } catch (LinkageError le) { abort(le, "java.launcher.cls.error6", cn, le.getClass().getName() + ": " + le.getLocalizedMessage()); + } catch (AccessControlException ace) { + abort(ace, "java.launcher.cls.error7", cn, + ace.getClass().getName(), ace.getLocalizedMessage()); } return mainClass; } --- old/src/java.base/share/native/libjava/Class.c 2019-09-04 11:46:08.000000000 -0700 +++ new/src/java.base/share/native/libjava/Class.c 2019-09-04 11:46:08.000000000 -0700 @@ -76,6 +76,7 @@ {"getRawTypeAnnotations", "()" BA, (void *)&JVM_GetClassTypeAnnotations}, {"getNestHost0", "()" CLS, (void *)&JVM_GetNestHost}, {"getNestMembers0", "()[" CLS, (void *)&JVM_GetNestMembers}, + {"linkClass", "(" CLS ")V", (void *)&JVM_LinkClass}, }; #undef OBJ --- old/test/hotspot/jtreg/gc/logging/TestMetaSpaceLog.java 2019-09-04 11:46:09.000000000 -0700 +++ new/test/hotspot/jtreg/gc/logging/TestMetaSpaceLog.java 2019-09-04 11:46:09.000000000 -0700 @@ -130,7 +130,7 @@ public static void loadClass(WhiteBox wb) { try { URLClassLoader ucl = new URLClassLoader(urls); - Class.forName("case00", false, ucl); + ucl.loadClass("case00"); } catch (Exception e) { e.printStackTrace(); } --- /dev/null 2019-09-04 11:46:10.000000000 -0700 +++ new/test/hotspot/jtreg/serviceability/jvmti/ClassStatus/ClassStatus.java 2019-09-04 11:46:10.000000000 -0700 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2019, 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 8212117 + * @summary Verify JVMTI GetClassStatus returns CLASS_PREPARE on class "Foo" + * @run main/othervm/native -agentlib:ClassStatus ClassStatus + */ + + +public class ClassStatus { + static { + try { + System.out.println("ClassStatus static block"); + System.loadLibrary("ClassStatus"); + } catch (UnsatisfiedLinkError ule) { + System.err.println("Could not load ClassStatus library"); + System.err.println("java.library.path: " + + System.getProperty("java.library.path")); + throw ule; + } + } + + // public static void foo(Foo f) { } + static native int check(Class klass); + + public static void main(String[] args) throws ClassNotFoundException { + ClassLoader loader = ClassStatus.class.getClassLoader(); + Module module = loader.getUnnamedModule(); + + // Load class, but don't initialize it + Class foo2 = Class.forName(module, "Foo2"); + Class foo3 = Class.forName("Foo3", false, loader); + + System.out.println("Loaded: " + foo2); + System.out.println("Loaded: " + foo3); + + int status2 = check(foo2); + int status3 = check(foo3); + + new Foo2().bar(); + new Foo3().bar(); + + if (status2 != 0) { + System.out.println("The agent returned non-zero exit status for Foo2: " + status2); + } + if (status3 != 0) { + System.out.println("The agent returned non-zero exit status for Foo3: " + status3); + } + if (status2 != 0 || status3 != 0) { + throw new RuntimeException("Non-zero status returned from the agent"); + } + } +} + +class Foo2 { + static { + System.out.println("Foo2 is initialized"); + } + void bar() { + System.out.println("Foo2.bar() is called"); + } +} + +class Foo3 { + static { + System.out.println("Foo3 is initialized"); + } + void bar() { + System.out.println("Foo3.bar() is called"); + } +} --- /dev/null 2019-09-04 11:46:11.000000000 -0700 +++ new/test/hotspot/jtreg/serviceability/jvmti/ClassStatus/libClassStatus.c 2019-09-04 11:46:11.000000000 -0700 @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2019, 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. + */ + +#include +#include +#include +#include +#include "jvmti.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef JNI_ENV_ARG + +#ifdef __cplusplus +#define JNI_ENV_ARG(x, y) y +#define JNI_ENV_PTR(x) x +#else +#define JNI_ENV_ARG(x,y) x, y +#define JNI_ENV_PTR(x) (*x) +#endif + +#endif + +#define PASSED 0 +#define FAILED 2 + +static jvmtiEnv* jvmti = NULL; + +static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved); + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { + return Agent_Initialize(jvm, options, reserved); +} + +JNIEXPORT jint JNICALL +Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { + return Agent_Initialize(jvm, options, reserved); +} + +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM *jvm, void *reserved) { + return JNI_VERSION_9; +} + +static void +check_jvmti_error(jvmtiEnv *jvmti, char* fname, jvmtiError err) { + if (err != JVMTI_ERROR_NONE) { + printf(" ## %s error: %d\n", fname, err); + fflush(0); + exit(err); + } +} + +static char* +get_class_signature(jvmtiEnv *jvmti, jclass klass) { + char* sign = NULL; + jvmtiError err = (*jvmti)->GetClassSignature(jvmti, klass, &sign, NULL); + + check_jvmti_error(jvmti, "GetClassSignature", err); + return sign; +} + +static jboolean +is_class_status_prepared(jvmtiEnv *jvmti, jclass klass) { + char* sign = get_class_signature(jvmti, klass); + jint status = 0; + jvmtiError err = (*jvmti)->GetClassStatus(jvmti, klass, &status); + + check_jvmti_error(jvmti, "GetClassStatus", err); + printf(" Class %s status: 0x%08x\n", sign, status); + printf(" Class %s is prepared: %d\n", sign, (status & JVMTI_CLASS_STATUS_PREPARED) != 0); + fflush(0); + + return (status & JVMTI_CLASS_STATUS_PREPARED) != 0; +} + +static jboolean +is_class_in_loaded_classes(JNIEnv *env, jclass klass) { + char* sign = get_class_signature(jvmti, klass); + jint class_count = 0; + jclass* classes = NULL; + jvmtiError err = (*jvmti)->GetLoadedClasses(jvmti, &class_count, &classes); + + check_jvmti_error(jvmti, "GetLoadedClasses", err); + + for (int i = 0; i < class_count; i++) { + jclass cls = classes[i]; + jboolean same = (*env)->IsSameObject(env, cls, klass); + if (same) { + printf("Found class %s in the list of loaded classes\n", sign); + fflush(0); + return JNI_TRUE; + } + } + printf("Error: Have not found class %s in the list of loaded classes\n", sign); + fflush(0); + return JNI_FALSE; +} + +static void JNICALL +ClassPrepare(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass) { + char* sign = get_class_signature(jvmti, klass); + + sign = (sign == NULL) ? "NULL" : sign; + + if (strcmp(sign, "LFoo2;") == 0 || strcmp(sign, "LFoo3;") == 0) { + printf("ClassPrepare event for class: %s\n", sign); + fflush(0); + } +} + +static jint +Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { + jvmtiError err; + jint size; + jint res; + jvmtiEventCallbacks callbacks; + + printf("Agent_Initialize started\n"); + fflush(0); + res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti), JVMTI_VERSION_9); + if (res != JNI_OK || jvmti == NULL) { + printf("## Agent_Initialize: Error in GetEnv: res: %d, jvmti env: %p\n", res, jvmti); + return JNI_ERR; + } + + size = (jint)sizeof(callbacks); + memset(&callbacks, 0, size); + callbacks.ClassPrepare = ClassPrepare; + + err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, size); + check_jvmti_error(jvmti, "## Agent_Initialize: SetEventCallbacks", err); + + err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, NULL); + check_jvmti_error(jvmti, "## Agent_Initialize: SetEventNotificationMode CLASS_PREPARE", err); + return JNI_OK; +} + +JNIEXPORT jint JNICALL +Java_ClassStatus_check(JNIEnv *env, jclass cls, jclass klass) { + if (is_class_in_loaded_classes(env, klass) != JNI_TRUE || + is_class_status_prepared(jvmti, klass) != JNI_TRUE) { + return FAILED; + } + return PASSED; +} + +#ifdef __cplusplus +} +#endif