8232613: Move Object.registerNatives into HotSpot
authorredestad
Thu, 24 Oct 2019 09:57:29 +0200
changeset 58775 ba524a5f7cc2
parent 58773 5bb426e9acc4
child 58776 ea153023d832
8232613: Move Object.registerNatives into HotSpot Reviewed-by: dholmes, adinn, coleenp, lfoltan, mchung
src/hotspot/share/classfile/javaClasses.cpp
src/hotspot/share/classfile/javaClasses.hpp
src/hotspot/share/classfile/systemDictionary.cpp
src/hotspot/share/classfile/systemDictionary.hpp
src/hotspot/share/oops/method.cpp
src/hotspot/share/oops/method.hpp
src/hotspot/share/prims/jni.cpp
src/java.base/share/classes/java/lang/Object.java
src/java.base/share/native/libjava/Object.c
test/hotspot/jtreg/compiler/dependencies/MonomorphicObjectCall/java.base/java/lang/Object.java
test/hotspot/jtreg/runtime/8024804/RegisterNatives.java
test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineObject.java
test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/bcinstr/BI04/bi04t002/newclass02/java.base/java/lang/Object.java
--- a/src/hotspot/share/classfile/javaClasses.cpp	Wed Oct 23 18:43:18 2019 -0700
+++ b/src/hotspot/share/classfile/javaClasses.cpp	Thu Oct 24 09:57:29 2019 +0200
@@ -87,6 +87,21 @@
   ALL_INJECTED_FIELDS(DECLARE_INJECTED_FIELD)
 };
 
+// Register native methods of Object
+void java_lang_Object::register_natives(TRAPS) {
+  InstanceKlass* obj = SystemDictionary::Object_klass();
+  Method::register_native(obj, vmSymbols::hashCode_name(),
+                          vmSymbols::void_int_signature(), (address) &JVM_IHashCode, CHECK);
+  Method::register_native(obj, vmSymbols::wait_name(),
+                          vmSymbols::long_void_signature(), (address) &JVM_MonitorWait, CHECK);
+  Method::register_native(obj, vmSymbols::notify_name(),
+                          vmSymbols::void_method_signature(), (address) &JVM_MonitorNotify, CHECK);
+  Method::register_native(obj, vmSymbols::notifyAll_name(),
+                          vmSymbols::void_method_signature(), (address) &JVM_MonitorNotifyAll, CHECK);
+  Method::register_native(obj, vmSymbols::clone_name(),
+                          vmSymbols::void_object_signature(), (address) &JVM_Clone, THREAD);
+}
+
 int JavaClasses::compute_injected_offset(InjectedFieldID id) {
   return _injected_fields[id].compute_offset();
 }
--- a/src/hotspot/share/classfile/javaClasses.hpp	Wed Oct 23 18:43:18 2019 -0700
+++ b/src/hotspot/share/classfile/javaClasses.hpp	Thu Oct 24 09:57:29 2019 +0200
@@ -88,6 +88,13 @@
         BASIC_JAVA_CLASSES_DO_PART1(f) \
         BASIC_JAVA_CLASSES_DO_PART2(f)
 
+// Interface to java.lang.Object objects
+
+class java_lang_Object : AllStatic {
+ public:
+  static void register_natives(TRAPS);
+};
+
 // Interface to java.lang.String objects
 
 class java_lang_String : AllStatic {
--- a/src/hotspot/share/classfile/systemDictionary.cpp	Wed Oct 23 18:43:18 2019 -0700
+++ b/src/hotspot/share/classfile/systemDictionary.cpp	Thu Oct 24 09:57:29 2019 +0200
@@ -1972,6 +1972,10 @@
     resolve_wk_klasses_through(WK_KLASS_ENUM_NAME(Class_klass), scan, CHECK);
   }
 
+  assert(WK_KLASS(Object_klass) != NULL, "well-known classes should now be initialized");
+
+  java_lang_Object::register_natives(CHECK);
+
   // Calculate offsets for String and Class classes since they are loaded and
   // can be used after this point.
   java_lang_String::compute_offsets();
--- a/src/hotspot/share/classfile/systemDictionary.hpp	Wed Oct 23 18:43:18 2019 -0700
+++ b/src/hotspot/share/classfile/systemDictionary.hpp	Thu Oct 24 09:57:29 2019 +0200
@@ -219,7 +219,6 @@
                                                                                                                 \
   /*end*/
 
-
 class SystemDictionary : AllStatic {
   friend class BootstrapInfo;
   friend class VMStructs;
@@ -383,7 +382,6 @@
     int limit = (int)end_id + 1;
     resolve_wk_klasses_until((WKID) limit, start_id, THREAD);
   }
-
 public:
   #define WK_KLASS_DECLARE(name, symbol) \
     static InstanceKlass* name() { return check_klass(_well_known_klasses[WK_KLASS_ENUM_NAME(name)]); } \
@@ -628,21 +626,6 @@
   // Basic find on classes in the midst of being loaded
   static Symbol* find_placeholder(Symbol* name, ClassLoaderData* loader_data);
 
-  // Add a placeholder for a class being loaded
-  static void add_placeholder(int index,
-                              Symbol* class_name,
-                              ClassLoaderData* loader_data);
-  static void remove_placeholder(int index,
-                                 Symbol* class_name,
-                                 ClassLoaderData* loader_data);
-
-  // Performs cleanups after resolve_super_or_fail. This typically needs
-  // to be called on failure.
-  // Won't throw, but can block.
-  static void resolution_cleanups(Symbol* class_name,
-                                  ClassLoaderData* loader_data,
-                                  TRAPS);
-
   // Resolve well-known classes so they can be used like SystemDictionary::String_klass()
   static void resolve_well_known_classes(TRAPS);
 
--- a/src/hotspot/share/oops/method.cpp	Wed Oct 23 18:43:18 2019 -0700
+++ b/src/hotspot/share/oops/method.cpp	Thu Oct 24 09:57:29 2019 +0200
@@ -25,6 +25,7 @@
 #include "precompiled.hpp"
 #include "classfile/classLoaderDataGraph.hpp"
 #include "classfile/metadataOnStackMark.hpp"
+#include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "code/codeCache.hpp"
 #include "code/debugInfoRec.hpp"
@@ -378,7 +379,83 @@
   assert(valid_itable_index(), "");
 }
 
+// The RegisterNatives call being attempted tried to register with a method that
+// is not native.  Ask JVM TI what prefixes have been specified.  Then check
+// to see if the native method is now wrapped with the prefixes.  See the
+// SetNativeMethodPrefix(es) functions in the JVM TI Spec for details.
+static Method* find_prefixed_native(Klass* k, Symbol* name, Symbol* signature, TRAPS) {
+#if INCLUDE_JVMTI
+  ResourceMark rm(THREAD);
+  Method* method;
+  int name_len = name->utf8_length();
+  char* name_str = name->as_utf8();
+  int prefix_count;
+  char** prefixes = JvmtiExport::get_all_native_method_prefixes(&prefix_count);
+  for (int i = 0; i < prefix_count; i++) {
+    char* prefix = prefixes[i];
+    int prefix_len = (int)strlen(prefix);
 
+    // try adding this prefix to the method name and see if it matches another method name
+    int trial_len = name_len + prefix_len;
+    char* trial_name_str = NEW_RESOURCE_ARRAY(char, trial_len + 1);
+    strcpy(trial_name_str, prefix);
+    strcat(trial_name_str, name_str);
+    TempNewSymbol trial_name = SymbolTable::probe(trial_name_str, trial_len);
+    if (trial_name == NULL) {
+      continue; // no such symbol, so this prefix wasn't used, try the next prefix
+    }
+    method = k->lookup_method(trial_name, signature);
+    if (method == NULL) {
+      continue; // signature doesn't match, try the next prefix
+    }
+    if (method->is_native()) {
+      method->set_is_prefixed_native();
+      return method; // wahoo, we found a prefixed version of the method, return it
+    }
+    // found as non-native, so prefix is good, add it, probably just need more prefixes
+    name_len = trial_len;
+    name_str = trial_name_str;
+  }
+#endif // INCLUDE_JVMTI
+  return NULL; // not found
+}
+
+bool Method::register_native(Klass* k, Symbol* name, Symbol* signature, address entry, TRAPS) {
+  Method* method = k->lookup_method(name, signature);
+  if (method == NULL) {
+    ResourceMark rm(THREAD);
+    stringStream st;
+    st.print("Method '");
+    print_external_name(&st, k, name, signature);
+    st.print("' name or signature does not match");
+    THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false);
+  }
+  if (!method->is_native()) {
+    // trying to register to a non-native method, see if a JVM TI agent has added prefix(es)
+    method = find_prefixed_native(k, name, signature, THREAD);
+    if (method == NULL) {
+      ResourceMark rm(THREAD);
+      stringStream st;
+      st.print("Method '");
+      print_external_name(&st, k, name, signature);
+      st.print("' is not declared as native");
+      THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false);
+    }
+  }
+
+  if (entry != NULL) {
+    method->set_native_function(entry, native_bind_event_is_interesting);
+  } else {
+    method->clear_native_function();
+  }
+  if (PrintJNIResolving) {
+    ResourceMark rm(THREAD);
+    tty->print_cr("[Registering JNI native method %s.%s]",
+      method->method_holder()->external_name(),
+      method->name()->as_C_string());
+  }
+  return true;
+}
 
 bool Method::was_executed_more_than(int n) {
   // Invocation counter is reset when the Method* is compiled.
--- a/src/hotspot/share/oops/method.hpp	Wed Oct 23 18:43:18 2019 -0700
+++ b/src/hotspot/share/oops/method.hpp	Thu Oct 24 09:57:29 2019 +0200
@@ -346,6 +346,12 @@
   // InterpreterRuntime::exception_handler_for_exception.
   static int fast_exception_handler_bci_for(const methodHandle& mh, Klass* ex_klass, int throw_bci, TRAPS);
 
+  static bool register_native(Klass* k,
+                              Symbol* name,
+                              Symbol* signature,
+                              address entry,
+                              TRAPS);
+
   // method data access
   MethodData* method_data() const              {
     return _method_data;
--- a/src/hotspot/share/prims/jni.cpp	Wed Oct 23 18:43:18 2019 -0700
+++ b/src/hotspot/share/prims/jni.cpp	Thu Oct 24 09:57:29 2019 +0200
@@ -2909,89 +2909,6 @@
                             HOTSPOT_JNI_SETDOUBLEARRAYREGION_RETURN())
 
 
-//
-// Interception of natives
-//
-
-// The RegisterNatives call being attempted tried to register with a method that
-// is not native.  Ask JVM TI what prefixes have been specified.  Then check
-// to see if the native method is now wrapped with the prefixes.  See the
-// SetNativeMethodPrefix(es) functions in the JVM TI Spec for details.
-static Method* find_prefixed_native(Klass* k, Symbol* name, Symbol* signature, TRAPS) {
-#if INCLUDE_JVMTI
-  ResourceMark rm(THREAD);
-  Method* method;
-  int name_len = name->utf8_length();
-  char* name_str = name->as_utf8();
-  int prefix_count;
-  char** prefixes = JvmtiExport::get_all_native_method_prefixes(&prefix_count);
-  for (int i = 0; i < prefix_count; i++) {
-    char* prefix = prefixes[i];
-    int prefix_len = (int)strlen(prefix);
-
-    // try adding this prefix to the method name and see if it matches another method name
-    int trial_len = name_len + prefix_len;
-    char* trial_name_str = NEW_RESOURCE_ARRAY(char, trial_len + 1);
-    strcpy(trial_name_str, prefix);
-    strcat(trial_name_str, name_str);
-    TempNewSymbol trial_name = SymbolTable::probe(trial_name_str, trial_len);
-    if (trial_name == NULL) {
-      continue; // no such symbol, so this prefix wasn't used, try the next prefix
-    }
-    method = k->lookup_method(trial_name, signature);
-    if (method == NULL) {
-      continue; // signature doesn't match, try the next prefix
-    }
-    if (method->is_native()) {
-      method->set_is_prefixed_native();
-      return method; // wahoo, we found a prefixed version of the method, return it
-    }
-    // found as non-native, so prefix is good, add it, probably just need more prefixes
-    name_len = trial_len;
-    name_str = trial_name_str;
-  }
-#endif // INCLUDE_JVMTI
-  return NULL; // not found
-}
-
-static bool register_native(Klass* k, Symbol* name, Symbol* signature, address entry, TRAPS) {
-  Method* method = k->lookup_method(name, signature);
-  if (method == NULL) {
-    ResourceMark rm;
-    stringStream st;
-    st.print("Method '");
-    Method::print_external_name(&st, k, name, signature);
-    st.print("' name or signature does not match");
-    THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false);
-  }
-  if (!method->is_native()) {
-    // trying to register to a non-native method, see if a JVM TI agent has added prefix(es)
-    method = find_prefixed_native(k, name, signature, THREAD);
-    if (method == NULL) {
-      ResourceMark rm;
-      stringStream st;
-      st.print("Method '");
-      Method::print_external_name(&st, k, name, signature);
-      st.print("' is not declared as native");
-      THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false);
-    }
-  }
-
-  if (entry != NULL) {
-    method->set_native_function(entry,
-      Method::native_bind_event_is_interesting);
-  } else {
-    method->clear_native_function();
-  }
-  if (PrintJNIResolving) {
-    ResourceMark rm(THREAD);
-    tty->print_cr("[Registering JNI native method %s.%s]",
-      method->method_holder()->external_name(),
-      method->name()->as_C_string());
-  }
-  return true;
-}
-
 DT_RETURN_MARK_DECL(RegisterNatives, jint
                     , HOTSPOT_JNI_REGISTERNATIVES_RETURN(_ret_ref));
 
@@ -3024,8 +2941,8 @@
       THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), -1);
     }
 
-    bool res = register_native(k, name, signature,
-                               (address) methods[index].fnPtr, THREAD);
+    bool res = Method::register_native(k, name, signature,
+                                       (address) methods[index].fnPtr, THREAD);
     if (!res) {
       ret = -1;
       break;
--- a/src/java.base/share/classes/java/lang/Object.java	Wed Oct 23 18:43:18 2019 -0700
+++ b/src/java.base/share/classes/java/lang/Object.java	Thu Oct 24 09:57:29 2019 +0200
@@ -38,11 +38,6 @@
  */
 public class Object {
 
-    private static native void registerNatives();
-    static {
-        registerNatives();
-    }
-
     /**
      * Constructs a new object.
      */
--- a/src/java.base/share/native/libjava/Object.c	Wed Oct 23 18:43:18 2019 -0700
+++ b/src/java.base/share/native/libjava/Object.c	Thu Oct 24 09:57:29 2019 +0200
@@ -39,21 +39,6 @@
 
 #include "java_lang_Object.h"
 
-static JNINativeMethod methods[] = {
-    {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
-    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
-    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
-    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
-    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
-};
-
-JNIEXPORT void JNICALL
-Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
-{
-    (*env)->RegisterNatives(env, cls,
-                            methods, sizeof(methods)/sizeof(methods[0]));
-}
-
 JNIEXPORT jclass JNICALL
 Java_java_lang_Object_getClass(JNIEnv *env, jobject this)
 {
--- a/test/hotspot/jtreg/compiler/dependencies/MonomorphicObjectCall/java.base/java/lang/Object.java	Wed Oct 23 18:43:18 2019 -0700
+++ b/test/hotspot/jtreg/compiler/dependencies/MonomorphicObjectCall/java.base/java/lang/Object.java	Thu Oct 24 09:57:29 2019 +0200
@@ -34,11 +34,6 @@
     @HotSpotIntrinsicCandidate
     public Object() {}
 
-    private static native void registerNatives();
-    static {
-        registerNatives();
-    }
-
     @HotSpotIntrinsicCandidate
     public final native Class<?> getClass();
 
--- a/test/hotspot/jtreg/runtime/8024804/RegisterNatives.java	Wed Oct 23 18:43:18 2019 -0700
+++ b/test/hotspot/jtreg/runtime/8024804/RegisterNatives.java	Thu Oct 24 09:57:29 2019 +0200
@@ -23,15 +23,27 @@
 
 /*
  * @test
- * @bug 8024804
- * @bug 8028741
+ * @bug 8024804 8028741 8232613
  * @summary interface method resolution should skip finding j.l.Object's registerNatives() and succeed in selecting class B's registerNatives()
  * @run main RegisterNatives
  */
 public class RegisterNatives {
-  interface I { void registerNatives(); }
+  interface I {
+    void registerNatives();
+  }
+
   interface J extends I {}
-  static class B implements J { public void registerNatives() { System.out.println("B"); } }
+
+  interface K {
+    default public void registerNatives() { System.out.println("K"); }
+  }
+
+  static class B implements J {
+    public void registerNatives() { System.out.println("B"); }
+  }
+
+  static class C implements K {}
+
   public static void main(String... args) {
     System.out.println("Regression test for JDK-8024804, crash when InterfaceMethodref resolves to Object.registerNatives\n");
     J val = new B();
@@ -42,6 +54,14 @@
       e.printStackTrace();
       throw e;
     }
+    C cval = new C();
+    try {
+      cval.registerNatives();
+    } catch (IllegalAccessError e) {
+      System.out.println("TEST FAILS - a default method named registerNatives should no longer be masked by removed Object.registerNatives\n");
+      e.printStackTrace();
+      throw e;
+    }
     System.out.println("TEST PASSES - no IAE resulted\n");
   }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineObject.java	Thu Oct 24 09:57:29 2019 +0200
@@ -0,0 +1,177 @@
+/*
+ * 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 8232613
+ * @summary Ensure Object natives stay registered after redefinition
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.base/jdk.internal.org.objectweb.asm
+ *          java.compiler
+ *          java.instrument
+ *          jdk.jartool/sun.tools.jar
+ * @run main RedefineObject buildagent
+ * @run main/othervm -javaagent:redefineagent.jar RedefineObject
+ */
+
+import static jdk.test.lib.Asserts.assertTrue;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.lang.RuntimeException;
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.lang.instrument.Instrumentation;
+import java.lang.instrument.UnmodifiableClassException;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+
+import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.ASM6;
+import static jdk.internal.org.objectweb.asm.Opcodes.V1_8;
+
+public class RedefineObject {
+
+    static Instrumentation inst;
+
+    public static void premain(String agentArgs, Instrumentation inst) {
+        RedefineObject.inst = inst;
+    }
+
+    static class Transformer implements ClassFileTransformer {
+
+        public byte[] asm(ClassLoader loader, String className,
+                          Class<?> classBeingRedefined,
+                          ProtectionDomain protectionDomain, byte[] classfileBuffer)
+                throws IllegalClassFormatException {
+            ClassWriter cw = new ClassWriter(0);
+            // Force an older ASM to force a bytecode update
+            ClassVisitor cv = new DummyClassVisitor(ASM6, cw) { };
+            ClassReader cr = new ClassReader(classfileBuffer);
+            cr.accept(cv, 0);
+            byte[] bytes = cw.toByteArray();
+            return bytes;
+        }
+
+        public class DummyClassVisitor extends ClassVisitor {
+
+            public DummyClassVisitor(int api, ClassVisitor cv) {
+                super(api, cv);
+            }
+
+            public void visit(
+                    final int version,
+                    final int access,
+                    final String name,
+                    final String signature,
+                    final String superName,
+                    final String[] interfaces) {
+                // Artificially lower to JDK 8 version to force a redefine
+                cv.visit(V1_8, access, name, signature, superName, interfaces);
+            }
+        }
+
+        @Override public byte[] transform(ClassLoader loader, String className,
+                                          Class<?> classBeingRedefined,
+                                          ProtectionDomain protectionDomain, byte[] classfileBuffer)
+                throws IllegalClassFormatException {
+
+            if (className.contains("java/lang/Object")) {
+                try {
+                    // Here we remove and re-add the dummy fields. This shuffles the constant pool
+                    return asm(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
+                } catch (Throwable e) {
+                    // The retransform native code that called this method does not propagate
+                    // exceptions. Instead of getting an uninformative generic error, catch
+                    // problems here and print it, then exit.
+                    e.printStackTrace();
+                    System.exit(1);
+                }
+            }
+            return null;
+        }
+    }
+
+    private static void buildAgent() {
+        try {
+            ClassFileInstaller.main("RedefineObject");
+        } catch (Exception e) {
+            throw new RuntimeException("Could not write agent classfile", e);
+        }
+
+        try {
+            PrintWriter pw = new PrintWriter("MANIFEST.MF");
+            pw.println("Premain-Class: RedefineObject");
+            pw.println("Agent-Class: RedefineObject");
+            pw.println("Can-Retransform-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", "RedefineObject.class" })) {
+            throw new RuntimeException("Could not write the agent jar file");
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+
+        int objHash = System.identityHashCode(Object.class);
+        System.out.println("Object hashCode: " + objHash);
+        if (args.length == 1 && args[0].equals("buildagent")) {
+            buildAgent();
+            return;
+        }
+
+        if (inst == null) {
+            throw new RuntimeException("Instrumentation object was null");
+        }
+
+        try {
+            inst.addTransformer(new RedefineObject.Transformer(), true);
+            inst.retransformClasses(Object.class);
+        } catch (UnmodifiableClassException e) {
+            throw new RuntimeException(e);
+        }
+
+        // Exercise native methods on Object after transform
+        Object b = new Object();
+        b.hashCode();
+
+        C c = new C();
+        assertTrue(c.hashCode() != c.clone().hashCode() || c != c.clone());
+        assertTrue(c.clone() instanceof C);
+        c = (C)c.clone(); // native method on new Object
+    }
+
+    private static class C implements Cloneable {
+        @Override
+        protected Object clone() throws CloneNotSupportedException {
+            return super.clone();
+        }
+    }
+}
--- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/bcinstr/BI04/bi04t002/newclass02/java.base/java/lang/Object.java	Wed Oct 23 18:43:18 2019 -0700
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/bcinstr/BI04/bi04t002/newclass02/java.base/java/lang/Object.java	Thu Oct 24 09:57:29 2019 +0200
@@ -37,11 +37,6 @@
  */
 public class Object {
 
-    private static native void registerNatives();
-    static {
-        registerNatives();
-    }
-
     /**
      * Returns the runtime class of an object. That <tt>Class</tt>
      * object is the object that is locked by <tt>static synchronized</tt>