8232613: Move Object.registerNatives into HotSpot
Reviewed-by: dholmes, adinn, coleenp, lfoltan, mchung
This commit is contained in:
parent
68e5c40f6f
commit
574263a884
@ -87,6 +87,21 @@ InjectedField JavaClasses::_injected_fields[] = {
|
||||
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();
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -1972,6 +1972,10 @@ void SystemDictionary::resolve_well_known_classes(TRAPS) {
|
||||
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();
|
||||
|
@ -219,7 +219,6 @@ class GCTimer;
|
||||
\
|
||||
/*end*/
|
||||
|
||||
|
||||
class SystemDictionary : AllStatic {
|
||||
friend class BootstrapInfo;
|
||||
friend class VMStructs;
|
||||
@ -383,7 +382,6 @@ public:
|
||||
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 @@ protected:
|
||||
// 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);
|
||||
|
||||
|
@ -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 @@ void Method::set_itable_index(int index) {
|
||||
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.
|
||||
|
@ -346,6 +346,12 @@ class Method : public Metadata {
|
||||
// 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;
|
||||
|
@ -2909,89 +2909,6 @@ DEFINE_SETSCALARARRAYREGION(T_DOUBLE, jdouble, Double, double
|
||||
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 @@ JNI_ENTRY(jint, jni_RegisterNatives(JNIEnv *env, jclass clazz,
|
||||
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;
|
||||
|
@ -38,11 +38,6 @@ import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
*/
|
||||
public class Object {
|
||||
|
||||
private static native void registerNatives();
|
||||
static {
|
||||
registerNatives();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new object.
|
||||
*/
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -34,11 +34,6 @@ public class Object {
|
||||
@HotSpotIntrinsicCandidate
|
||||
public Object() {}
|
||||
|
||||
private static native void registerNatives();
|
||||
static {
|
||||
registerNatives();
|
||||
}
|
||||
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final native Class<?> getClass();
|
||||
|
||||
|
@ -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 @@ public class RegisterNatives {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -37,11 +37,6 @@ import nsk.jvmti.scenarios.bcinstr.BI04.bi04t002a;
|
||||
*/
|
||||
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>
|
||||
|
Loading…
Reference in New Issue
Block a user