提交 b0afd96e 编写于 作者: C coleenp

8004728: Add hotspot support for parameter reflection

Summary: Add hotspot support for parameter reflection
Reviewed-by: acorn, jrose, coleenp
Contributed-by: eric.mccorkle@oracle.com
上级 694ed223
#
# @(#)mapfile-vers-debug 1.18 07/10/25 16:47:35
# @(#)mapfile-vers-debug 1.18 07/10/25 16:47:35
#
#
......@@ -126,7 +126,7 @@ SUNWprivate_1.1 {
JVM_GetClassModifiers;
JVM_GetClassName;
JVM_GetClassNameUTF;
JVM_GetClassSignature;
JVM_GetClassSignature;
JVM_GetClassSigners;
JVM_GetClassTypeAnnotations;
JVM_GetComponentType;
......@@ -155,6 +155,7 @@ SUNWprivate_1.1 {
JVM_GetMethodIxNameUTF;
JVM_GetMethodIxSignatureUTF;
JVM_GetMethodParameterAnnotations;
JVM_GetMethodParameters;
JVM_GetPrimitiveArrayElement;
JVM_GetProtectionDomain;
JVM_GetSockName;
......@@ -284,7 +285,7 @@ SUNWprivate_1.1 {
# This is for Forte Analyzer profiling support.
AsyncGetCallTrace;
# INSERT VTABLE SYMBOLS HERE
# INSERT VTABLE SYMBOLS HERE
local:
*;
......
......@@ -155,6 +155,7 @@ SUNWprivate_1.1 {
JVM_GetMethodIxNameUTF;
JVM_GetMethodIxSignatureUTF;
JVM_GetMethodParameterAnnotations;
JVM_GetMethodParameters;
JVM_GetPrimitiveArrayElement;
JVM_GetProtectionDomain;
JVM_GetSockName;
......
......@@ -151,6 +151,7 @@ SUNWprivate_1.1 {
JVM_GetMethodIxNameUTF;
JVM_GetMethodIxSignatureUTF;
JVM_GetMethodParameterAnnotations;
JVM_GetMethodParameters;
JVM_GetPrimitiveArrayElement;
JVM_GetProtectionDomain;
JVM_GetSockName;
......
......@@ -151,6 +151,7 @@ SUNWprivate_1.1 {
JVM_GetMethodIxNameUTF;
JVM_GetMethodIxSignatureUTF;
JVM_GetMethodParameterAnnotations;
JVM_GetMethodParameters;
JVM_GetPrimitiveArrayElement;
JVM_GetProtectionDomain;
JVM_GetSockName;
......
......@@ -26,236 +26,237 @@
SUNWprivate_1.1 {
global:
# JNI
# JNI
JNI_CreateJavaVM;
JNI_GetCreatedJavaVMs;
JNI_GetDefaultJavaVMInitArgs;
# JVM
JVM_Accept;
JVM_ActiveProcessorCount;
JVM_AllocateNewArray;
JVM_AllocateNewObject;
JVM_ArrayCopy;
JVM_AssertionStatusDirectives;
JVM_Available;
JVM_Bind;
JVM_ClassDepth;
JVM_ClassLoaderDepth;
JVM_Clone;
JVM_Close;
JVM_CX8Field;
JVM_CompileClass;
JVM_CompileClasses;
JVM_CompilerCommand;
JVM_Connect;
JVM_ConstantPoolGetClassAt;
JVM_ConstantPoolGetClassAtIfLoaded;
JVM_ConstantPoolGetDoubleAt;
JVM_ConstantPoolGetFieldAt;
JVM_ConstantPoolGetFieldAtIfLoaded;
JVM_ConstantPoolGetFloatAt;
JVM_ConstantPoolGetIntAt;
JVM_ConstantPoolGetLongAt;
JVM_ConstantPoolGetMethodAt;
JVM_ConstantPoolGetMethodAtIfLoaded;
JVM_ConstantPoolGetMemberRefInfoAt;
JVM_ConstantPoolGetSize;
JVM_ConstantPoolGetStringAt;
JVM_ConstantPoolGetUTF8At;
JVM_CountStackFrames;
JVM_CurrentClassLoader;
JVM_CurrentLoadedClass;
JVM_CurrentThread;
JVM_CurrentTimeMillis;
JVM_DefineClass;
JVM_DefineClassWithSource;
JVM_DefineClassWithSourceCond;
JVM_DesiredAssertionStatus;
JVM_DisableCompiler;
JVM_DoPrivileged;
JVM_DTraceGetVersion;
JVM_DTraceActivate;
JVM_DTraceIsProbeEnabled;
JVM_DTraceIsSupported;
JVM_DTraceDispose;
JVM_DumpAllStacks;
JVM_DumpThreads;
JVM_EnableCompiler;
JVM_Exit;
JVM_FillInStackTrace;
JVM_FindClassFromClass;
JVM_FindClassFromClassLoader;
JVM_FindClassFromBootLoader;
JVM_FindLibraryEntry;
JVM_FindLoadedClass;
JVM_FindPrimitiveClass;
JVM_FindSignal;
JVM_FreeMemory;
JVM_GC;
JVM_GetAllThreads;
JVM_GetArrayElement;
JVM_GetArrayLength;
JVM_GetCPClassNameUTF;
JVM_GetCPFieldClassNameUTF;
JVM_GetCPFieldModifiers;
JVM_GetCPFieldNameUTF;
JVM_GetCPFieldSignatureUTF;
JVM_GetCPMethodClassNameUTF;
JVM_GetCPMethodModifiers;
JVM_GetCPMethodNameUTF;
JVM_GetCPMethodSignatureUTF;
JVM_GetCallerClass;
JVM_GetClassAccessFlags;
JVM_GetClassAnnotations;
JVM_GetClassCPEntriesCount;
JVM_GetClassCPTypes;
JVM_GetClassConstantPool;
JVM_GetClassContext;
JVM_GetClassDeclaredConstructors;
JVM_GetClassDeclaredFields;
JVM_GetClassDeclaredMethods;
JVM_GetClassFieldsCount;
JVM_GetClassInterfaces;
JVM_GetClassLoader;
JVM_GetClassMethodsCount;
JVM_GetClassModifiers;
JVM_GetClassName;
JVM_GetClassNameUTF;
JVM_GetClassSignature;
JVM_GetClassSigners;
JVM_GetComponentType;
JVM_GetClassTypeAnnotations;
JVM_GetDeclaredClasses;
JVM_GetDeclaringClass;
JVM_GetEnclosingMethodInfo;
JVM_GetFieldAnnotations;
JVM_GetFieldIxModifiers;
JVM_GetHostName;
JVM_GetInheritedAccessControlContext;
JVM_GetInterfaceVersion;
JVM_GetLastErrorString;
JVM_GetManagement;
JVM_GetMethodAnnotations;
JVM_GetMethodDefaultAnnotationValue;
JVM_GetMethodIxArgsSize;
JVM_GetMethodIxByteCode;
JVM_GetMethodIxByteCodeLength;
JVM_GetMethodIxExceptionIndexes;
JVM_GetMethodIxExceptionTableEntry;
JVM_GetMethodIxExceptionTableLength;
JVM_GetMethodIxExceptionsCount;
JVM_GetMethodIxLocalsCount;
JVM_GetMethodIxMaxStack;
JVM_GetMethodIxModifiers;
JVM_GetMethodIxNameUTF;
JVM_GetMethodIxSignatureUTF;
JVM_GetMethodParameterAnnotations;
JVM_GetPrimitiveArrayElement;
JVM_GetProtectionDomain;
JVM_GetSockName;
JVM_GetSockOpt;
JVM_GetStackAccessControlContext;
JVM_GetStackTraceDepth;
JVM_GetStackTraceElement;
JVM_GetSystemPackage;
JVM_GetSystemPackages;
JVM_GetThreadStateNames;
JVM_GetThreadStateValues;
JVM_GetVersionInfo;
JVM_Halt;
JVM_HoldsLock;
JVM_IHashCode;
JVM_InitAgentProperties;
JVM_InitProperties;
JVM_InitializeCompiler;
JVM_InitializeSocketLibrary;
JVM_InternString;
JVM_Interrupt;
JVM_InvokeMethod;
JVM_IsArrayClass;
JVM_IsConstructorIx;
JVM_IsInterface;
JVM_IsInterrupted;
JVM_IsNaN;
JVM_IsPrimitiveClass;
JVM_IsSameClassPackage;
JVM_IsSilentCompiler;
JVM_IsSupportedJNIVersion;
JVM_IsThreadAlive;
JVM_LatestUserDefinedLoader;
JVM_Listen;
JVM_LoadClass0;
JVM_LoadLibrary;
JVM_Lseek;
JVM_MaxObjectInspectionAge;
JVM_MaxMemory;
JVM_MonitorNotify;
JVM_MonitorNotifyAll;
JVM_MonitorWait;
JVM_NativePath;
JVM_NanoTime;
JVM_NewArray;
JVM_NewInstanceFromConstructor;
JVM_NewMultiArray;
JVM_OnExit;
JVM_Open;
JVM_PrintStackTrace;
JVM_RaiseSignal;
JVM_RawMonitorCreate;
JVM_RawMonitorDestroy;
JVM_RawMonitorEnter;
JVM_RawMonitorExit;
JVM_Read;
JVM_Recv;
JVM_RecvFrom;
JVM_RegisterSignal;
JVM_ReleaseUTF;
JVM_ResolveClass;
JVM_ResumeThread;
JVM_Send;
JVM_SendTo;
JVM_SetArrayElement;
JVM_SetClassSigners;
JVM_SetLength;
# JVM
JVM_Accept;
JVM_ActiveProcessorCount;
JVM_AllocateNewArray;
JVM_AllocateNewObject;
JVM_ArrayCopy;
JVM_AssertionStatusDirectives;
JVM_Available;
JVM_Bind;
JVM_ClassDepth;
JVM_ClassLoaderDepth;
JVM_Clone;
JVM_Close;
JVM_CX8Field;
JVM_CompileClass;
JVM_CompileClasses;
JVM_CompilerCommand;
JVM_Connect;
JVM_ConstantPoolGetClassAt;
JVM_ConstantPoolGetClassAtIfLoaded;
JVM_ConstantPoolGetDoubleAt;
JVM_ConstantPoolGetFieldAt;
JVM_ConstantPoolGetFieldAtIfLoaded;
JVM_ConstantPoolGetFloatAt;
JVM_ConstantPoolGetIntAt;
JVM_ConstantPoolGetLongAt;
JVM_ConstantPoolGetMethodAt;
JVM_ConstantPoolGetMethodAtIfLoaded;
JVM_ConstantPoolGetMemberRefInfoAt;
JVM_ConstantPoolGetSize;
JVM_ConstantPoolGetStringAt;
JVM_ConstantPoolGetUTF8At;
JVM_CountStackFrames;
JVM_CurrentClassLoader;
JVM_CurrentLoadedClass;
JVM_CurrentThread;
JVM_CurrentTimeMillis;
JVM_DefineClass;
JVM_DefineClassWithSource;
JVM_DefineClassWithSourceCond;
JVM_DesiredAssertionStatus;
JVM_DisableCompiler;
JVM_DoPrivileged;
JVM_DTraceGetVersion;
JVM_DTraceActivate;
JVM_DTraceIsProbeEnabled;
JVM_DTraceIsSupported;
JVM_DTraceDispose;
JVM_DumpAllStacks;
JVM_DumpThreads;
JVM_EnableCompiler;
JVM_Exit;
JVM_FillInStackTrace;
JVM_FindClassFromClass;
JVM_FindClassFromClassLoader;
JVM_FindClassFromBootLoader;
JVM_FindLibraryEntry;
JVM_FindLoadedClass;
JVM_FindPrimitiveClass;
JVM_FindSignal;
JVM_FreeMemory;
JVM_GC;
JVM_GetAllThreads;
JVM_GetArrayElement;
JVM_GetArrayLength;
JVM_GetCPClassNameUTF;
JVM_GetCPFieldClassNameUTF;
JVM_GetCPFieldModifiers;
JVM_GetCPFieldNameUTF;
JVM_GetCPFieldSignatureUTF;
JVM_GetCPMethodClassNameUTF;
JVM_GetCPMethodModifiers;
JVM_GetCPMethodNameUTF;
JVM_GetCPMethodSignatureUTF;
JVM_GetCallerClass;
JVM_GetClassAccessFlags;
JVM_GetClassAnnotations;
JVM_GetClassCPEntriesCount;
JVM_GetClassCPTypes;
JVM_GetClassConstantPool;
JVM_GetClassContext;
JVM_GetClassDeclaredConstructors;
JVM_GetClassDeclaredFields;
JVM_GetClassDeclaredMethods;
JVM_GetClassFieldsCount;
JVM_GetClassInterfaces;
JVM_GetClassLoader;
JVM_GetClassMethodsCount;
JVM_GetClassModifiers;
JVM_GetClassName;
JVM_GetClassNameUTF;
JVM_GetClassSignature;
JVM_GetClassSigners;
JVM_GetComponentType;
JVM_GetClassTypeAnnotations;
JVM_GetDeclaredClasses;
JVM_GetDeclaringClass;
JVM_GetEnclosingMethodInfo;
JVM_GetFieldAnnotations;
JVM_GetFieldIxModifiers;
JVM_GetHostName;
JVM_GetInheritedAccessControlContext;
JVM_GetInterfaceVersion;
JVM_GetLastErrorString;
JVM_GetManagement;
JVM_GetMethodAnnotations;
JVM_GetMethodDefaultAnnotationValue;
JVM_GetMethodIxArgsSize;
JVM_GetMethodIxByteCode;
JVM_GetMethodIxByteCodeLength;
JVM_GetMethodIxExceptionIndexes;
JVM_GetMethodIxExceptionTableEntry;
JVM_GetMethodIxExceptionTableLength;
JVM_GetMethodIxExceptionsCount;
JVM_GetMethodIxLocalsCount;
JVM_GetMethodIxMaxStack;
JVM_GetMethodIxModifiers;
JVM_GetMethodIxNameUTF;
JVM_GetMethodIxSignatureUTF;
JVM_GetMethodParameterAnnotations;
JVM_GetMethodParameters;
JVM_GetPrimitiveArrayElement;
JVM_GetProtectionDomain;
JVM_GetSockName;
JVM_GetSockOpt;
JVM_GetStackAccessControlContext;
JVM_GetStackTraceDepth;
JVM_GetStackTraceElement;
JVM_GetSystemPackage;
JVM_GetSystemPackages;
JVM_GetThreadStateNames;
JVM_GetThreadStateValues;
JVM_GetVersionInfo;
JVM_Halt;
JVM_HoldsLock;
JVM_IHashCode;
JVM_InitAgentProperties;
JVM_InitProperties;
JVM_InitializeCompiler;
JVM_InitializeSocketLibrary;
JVM_InternString;
JVM_Interrupt;
JVM_InvokeMethod;
JVM_IsArrayClass;
JVM_IsConstructorIx;
JVM_IsInterface;
JVM_IsInterrupted;
JVM_IsNaN;
JVM_IsPrimitiveClass;
JVM_IsSameClassPackage;
JVM_IsSilentCompiler;
JVM_IsSupportedJNIVersion;
JVM_IsThreadAlive;
JVM_LatestUserDefinedLoader;
JVM_Listen;
JVM_LoadClass0;
JVM_LoadLibrary;
JVM_Lseek;
JVM_MaxObjectInspectionAge;
JVM_MaxMemory;
JVM_MonitorNotify;
JVM_MonitorNotifyAll;
JVM_MonitorWait;
JVM_NativePath;
JVM_NanoTime;
JVM_NewArray;
JVM_NewInstanceFromConstructor;
JVM_NewMultiArray;
JVM_OnExit;
JVM_Open;
JVM_PrintStackTrace;
JVM_RaiseSignal;
JVM_RawMonitorCreate;
JVM_RawMonitorDestroy;
JVM_RawMonitorEnter;
JVM_RawMonitorExit;
JVM_Read;
JVM_Recv;
JVM_RecvFrom;
JVM_RegisterSignal;
JVM_ReleaseUTF;
JVM_ResolveClass;
JVM_ResumeThread;
JVM_Send;
JVM_SendTo;
JVM_SetArrayElement;
JVM_SetClassSigners;
JVM_SetLength;
JVM_SetNativeThreadName;
JVM_SetPrimitiveArrayElement;
JVM_SetProtectionDomain;
JVM_SetSockOpt;
JVM_SetThreadPriority;
JVM_Sleep;
JVM_Socket;
JVM_SocketAvailable;
JVM_SocketClose;
JVM_SocketShutdown;
JVM_StartThread;
JVM_StopThread;
JVM_SuspendThread;
JVM_SupportsCX8;
JVM_Sync;
JVM_Timeout;
JVM_TotalMemory;
JVM_TraceInstructions;
JVM_TraceMethodCalls;
JVM_UnloadLibrary;
JVM_Write;
JVM_Yield;
JVM_handle_solaris_signal;
JVM_SetPrimitiveArrayElement;
JVM_SetProtectionDomain;
JVM_SetSockOpt;
JVM_SetThreadPriority;
JVM_Sleep;
JVM_Socket;
JVM_SocketAvailable;
JVM_SocketClose;
JVM_SocketShutdown;
JVM_StartThread;
JVM_StopThread;
JVM_SuspendThread;
JVM_SupportsCX8;
JVM_Sync;
JVM_Timeout;
JVM_TotalMemory;
JVM_TraceInstructions;
JVM_TraceMethodCalls;
JVM_UnloadLibrary;
JVM_Write;
JVM_Yield;
JVM_handle_solaris_signal;
# miscellaneous functions
jio_fprintf;
jio_printf;
jio_snprintf;
jio_vfprintf;
jio_vsnprintf;
# miscellaneous functions
jio_fprintf;
jio_printf;
jio_snprintf;
jio_vfprintf;
jio_vsnprintf;
# Needed because there is no JVM interface for this.
sysThreadAvailableStackWithSlack;
# Needed because there is no JVM interface for this.
sysThreadAvailableStackWithSlack;
# This is for Forte Analyzer profiling support.
AsyncGetCallTrace;
# This is for Forte Analyzer profiling support.
AsyncGetCallTrace;
# INSERT VTABLE SYMBOLS HERE
# INSERT VTABLE SYMBOLS HERE
local:
*;
......
......@@ -1935,6 +1935,8 @@ methodHandle ClassFileParser::parse_method(ClassLoaderData* loader_data,
u2** localvariable_table_start;
u2* localvariable_type_table_length;
u2** localvariable_type_table_start;
u2 method_parameters_length = 0;
u1* method_parameters_data = NULL;
bool parsed_code_attribute = false;
bool parsed_checked_exceptions_attribute = false;
bool parsed_stackmap_attribute = false;
......@@ -2144,6 +2146,14 @@ methodHandle ClassFileParser::parse_method(ClassLoaderData* loader_data,
parse_checked_exceptions(&checked_exceptions_length,
method_attribute_length,
cp, CHECK_(nullHandle));
} else if (method_attribute_name == vmSymbols::tag_method_parameters()) {
method_parameters_length = cfs->get_u1_fast();
method_parameters_data = cfs->get_u1_buffer();
cfs->skip_u2_fast(method_parameters_length);
cfs->skip_u4_fast(method_parameters_length);
// ignore this attribute if it cannot be reflected
if (!SystemDictionary::Parameter_klass_loaded())
method_parameters_length = 0;
} else if (method_attribute_name == vmSymbols::tag_synthetic()) {
if (method_attribute_length != 0) {
classfile_parse_error(
......@@ -2231,7 +2241,8 @@ methodHandle ClassFileParser::parse_method(ClassLoaderData* loader_data,
Method* m = Method::allocate(
loader_data, code_length, access_flags, linenumber_table_length,
total_lvt_length, exception_table_length, checked_exceptions_length,
generic_signature_index, ConstMethod::NORMAL, CHECK_(nullHandle));
method_parameters_length, generic_signature_index,
ConstMethod::NORMAL, CHECK_(nullHandle));
ClassLoadingService::add_class_method_size(m->size()*HeapWordSize);
......@@ -2279,6 +2290,18 @@ methodHandle ClassFileParser::parse_method(ClassLoaderData* loader_data,
exception_table_start, size);
}
// Copy method parameters
if (method_parameters_length > 0) {
MethodParametersElement* elem = m->constMethod()->method_parameters_start();
for(int i = 0; i < method_parameters_length; i++) {
elem[i].name_cp_index =
Bytes::get_Java_u2(method_parameters_data);
method_parameters_data += 2;
elem[i].flags = Bytes::get_Java_u4(method_parameters_data);
method_parameters_data += 4;
}
}
// Copy checked exceptions
if (checked_exceptions_length > 0) {
int size = checked_exceptions_length * sizeof(CheckedExceptionElement) / sizeof(u2);
......@@ -3042,6 +3065,7 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name,
TempNewSymbol& parsed_name,
bool verify,
TRAPS) {
// When a retransformable agent is attached, JVMTI caches the
// class bytes that existed before the first retransformation.
// If RedefineClasses() was used before the retransformable
......
......@@ -93,3 +93,10 @@ void ClassFileStream::skip_u2(int length, TRAPS) {
}
_current += length * 2;
}
void ClassFileStream::skip_u4(int length, TRAPS) {
if (_need_verify) {
guarantee_more(length * 4, CHECK);
}
_current += length * 4;
}
......@@ -133,6 +133,11 @@ class ClassFileStream: public ResourceObj {
_current += 2 * length;
}
void skip_u4(int length, TRAPS);
void skip_u4_fast(int length) {
_current += 4 * length;
}
// Tells whether eos is reached
bool at_eos() const { return _current == _buffer_end; }
};
......
......@@ -1148,7 +1148,8 @@ static Method* new_method(
int code_length = bytecodes->length();
Method* m = Method::allocate(cp->pool_holder()->class_loader_data(),
code_length, flags, 0, 0, 0, 0, 0, mt, CHECK_NULL);
code_length, flags, 0, 0, 0, 0, 0, 0,
mt, CHECK_NULL);
m->set_constants(NULL); // This will get filled in later
m->set_name_index(cp->utf8(name));
......
......@@ -2255,6 +2255,66 @@ void sun_reflect_ConstantPool::compute_offsets() {
}
}
void java_lang_reflect_Parameter::compute_offsets() {
Klass* k = SystemDictionary::reflect_Parameter_klass();
if(NULL != k) {
compute_offset(name_offset, k, vmSymbols::name_name(), vmSymbols::string_signature());
compute_offset(modifiers_offset, k, vmSymbols::modifiers_name(), vmSymbols::int_signature());
compute_offset(index_offset, k, vmSymbols::index_name(), vmSymbols::int_signature());
compute_offset(executable_offset, k, vmSymbols::executable_name(), vmSymbols::executable_signature());
}
}
Handle java_lang_reflect_Parameter::create(TRAPS) {
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem");
Symbol* name = vmSymbols::java_lang_reflect_Parameter();
Klass* k = SystemDictionary::resolve_or_fail(name, true, CHECK_NH);
instanceKlassHandle klass (THREAD, k);
// Ensure it is initialized
klass->initialize(CHECK_NH);
return klass->allocate_instance_handle(CHECK_NH);
}
oop java_lang_reflect_Parameter::name(oop param) {
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem");
return param->obj_field(name_offset);
}
void java_lang_reflect_Parameter::set_name(oop param, oop value) {
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem");
param->obj_field_put(name_offset, value);
}
int java_lang_reflect_Parameter::modifiers(oop param) {
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem");
return param->int_field(modifiers_offset);
}
void java_lang_reflect_Parameter::set_modifiers(oop param, int value) {
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem");
param->int_field_put(modifiers_offset, value);
}
int java_lang_reflect_Parameter::index(oop param) {
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem");
return param->int_field(index_offset);
}
void java_lang_reflect_Parameter::set_index(oop param, int value) {
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem");
param->int_field_put(index_offset, value);
}
oop java_lang_reflect_Parameter::executable(oop param) {
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem");
return param->obj_field(executable_offset);
}
void java_lang_reflect_Parameter::set_executable(oop param, oop value) {
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem");
param->obj_field_put(executable_offset, value);
}
Handle sun_reflect_ConstantPool::create(TRAPS) {
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem");
......@@ -2928,6 +2988,10 @@ int java_lang_reflect_Field::modifiers_offset;
int java_lang_reflect_Field::signature_offset;
int java_lang_reflect_Field::annotations_offset;
int java_lang_reflect_Field::type_annotations_offset;
int java_lang_reflect_Parameter::name_offset;
int java_lang_reflect_Parameter::modifiers_offset;
int java_lang_reflect_Parameter::index_offset;
int java_lang_reflect_Parameter::executable_offset;
int java_lang_boxing_object::value_offset;
int java_lang_boxing_object::long_value_offset;
int java_lang_ref_Reference::referent_offset;
......@@ -3112,6 +3176,8 @@ void JavaClasses::compute_offsets() {
sun_reflect_ConstantPool::compute_offsets();
sun_reflect_UnsafeStaticFieldAccessorImpl::compute_offsets();
}
if (JDK_Version::is_jdk18x_version())
java_lang_reflect_Parameter::compute_offsets();
// generated interpreter code wants to know about the offsets we just computed:
AbstractAssembler::update_delayed_values();
......
......@@ -729,6 +729,37 @@ class java_lang_reflect_Field : public java_lang_reflect_AccessibleObject {
friend class JavaClasses;
};
class java_lang_reflect_Parameter {
private:
// Note that to reduce dependencies on the JDK we compute these
// offsets at run-time.
static int name_offset;
static int modifiers_offset;
static int index_offset;
static int executable_offset;
static void compute_offsets();
public:
// Allocation
static Handle create(TRAPS);
// Accessors
static oop name(oop field);
static void set_name(oop field, oop value);
static int index(oop reflect);
static void set_index(oop reflect, int value);
static int modifiers(oop reflect);
static void set_modifiers(oop reflect, int value);
static oop executable(oop constructor);
static void set_executable(oop constructor, oop value);
friend class JavaClasses;
};
// Interface to sun.reflect.ConstantPool objects
class sun_reflect_ConstantPool {
private:
......
......@@ -131,6 +131,7 @@ class SymbolPropertyTable;
do_klass(Properties_klass, java_util_Properties, Pre ) \
do_klass(reflect_AccessibleObject_klass, java_lang_reflect_AccessibleObject, Pre ) \
do_klass(reflect_Field_klass, java_lang_reflect_Field, Pre ) \
do_klass(reflect_Parameter_klass, java_lang_reflect_Parameter, Opt ) \
do_klass(reflect_Method_klass, java_lang_reflect_Method, Pre ) \
do_klass(reflect_Constructor_klass, java_lang_reflect_Constructor, Pre ) \
\
......@@ -459,6 +460,7 @@ public:
// Tells whether ClassLoader.checkPackageAccess is present
static bool has_checkPackageAccess() { return _has_checkPackageAccess; }
static bool Parameter_klass_loaded() { return WK_KLASS(reflect_Parameter_klass) != NULL; }
static bool Class_klass_loaded() { return WK_KLASS(Class_klass) != NULL; }
static bool Cloneable_klass_loaded() { return WK_KLASS(Cloneable_klass) != NULL; }
static bool Object_klass_loaded() { return WK_KLASS(Object_klass) != NULL; }
......
......@@ -86,6 +86,7 @@
template(java_lang_reflect_Method, "java/lang/reflect/Method") \
template(java_lang_reflect_Constructor, "java/lang/reflect/Constructor") \
template(java_lang_reflect_Field, "java/lang/reflect/Field") \
template(java_lang_reflect_Parameter, "java/lang/reflect/Parameter") \
template(java_lang_reflect_Array, "java/lang/reflect/Array") \
template(java_lang_StringBuffer, "java/lang/StringBuffer") \
template(java_lang_StringBuilder, "java/lang/StringBuilder") \
......@@ -126,6 +127,7 @@
template(tag_line_number_table, "LineNumberTable") \
template(tag_local_variable_table, "LocalVariableTable") \
template(tag_local_variable_type_table, "LocalVariableTypeTable") \
template(tag_method_parameters, "MethodParameters") \
template(tag_stack_map_table, "StackMapTable") \
template(tag_synthetic, "Synthetic") \
template(tag_deprecated, "Deprecated") \
......@@ -235,6 +237,8 @@
/* Support for annotations (JDK 1.5 and above) */ \
\
template(annotations_name, "annotations") \
template(index_name, "index") \
template(executable_name, "executable") \
template(parameter_annotations_name, "parameterAnnotations") \
template(annotation_default_name, "annotationDefault") \
template(sun_reflect_ConstantPool, "sun/reflect/ConstantPool") \
......@@ -475,6 +479,7 @@
template(class_signature, "Ljava/lang/Class;") \
template(string_signature, "Ljava/lang/String;") \
template(reference_signature, "Ljava/lang/ref/Reference;") \
template(executable_signature, "Ljava/lang/reflect/Executable;") \
template(concurrenthashmap_signature, "Ljava/util/concurrent/ConcurrentHashMap;") \
template(String_StringBuilder_signature, "(Ljava/lang/String;)Ljava/lang/StringBuilder;") \
template(int_StringBuilder_signature, "(I)Ljava/lang/StringBuilder;") \
......
......@@ -39,18 +39,21 @@ ConstMethod* ConstMethod::allocate(ClassLoaderData* loader_data,
int localvariable_table_length,
int exception_table_length,
int checked_exceptions_length,
int method_parameters_length,
u2 generic_signature_index,
MethodType method_type,
TRAPS) {
int size = ConstMethod::size(byte_code_size,
compressed_line_number_size,
localvariable_table_length,
exception_table_length,
checked_exceptions_length,
generic_signature_index);
compressed_line_number_size,
localvariable_table_length,
exception_table_length,
checked_exceptions_length,
method_parameters_length,
generic_signature_index);
return new (loader_data, size, true, THREAD) ConstMethod(
byte_code_size, compressed_line_number_size, localvariable_table_length,
exception_table_length, checked_exceptions_length, generic_signature_index,
exception_table_length, checked_exceptions_length,
method_parameters_length, generic_signature_index,
method_type, size);
}
......@@ -59,6 +62,7 @@ ConstMethod::ConstMethod(int byte_code_size,
int localvariable_table_length,
int exception_table_length,
int checked_exceptions_length,
int method_parameters_length,
u2 generic_signature_index,
MethodType method_type,
int size) {
......@@ -74,7 +78,8 @@ ConstMethod::ConstMethod(int byte_code_size,
checked_exceptions_length,
compressed_line_number_size,
localvariable_table_length,
exception_table_length);
exception_table_length,
method_parameters_length);
set_method_type(method_type);
assert(this->size() == size, "wrong size for object");
}
......@@ -92,11 +97,12 @@ void ConstMethod::deallocate_contents(ClassLoaderData* loader_data) {
// How big must this constMethodObject be?
int ConstMethod::size(int code_size,
int compressed_line_number_size,
int local_variable_table_length,
int exception_table_length,
int checked_exceptions_length,
u2 generic_signature_index) {
int compressed_line_number_size,
int local_variable_table_length,
int exception_table_length,
int checked_exceptions_length,
int method_parameters_length,
u2 generic_signature_index) {
int extra_bytes = code_size;
if (compressed_line_number_size > 0) {
extra_bytes += compressed_line_number_size;
......@@ -117,6 +123,10 @@ int ConstMethod::size(int code_size,
if (generic_signature_index != 0) {
extra_bytes += sizeof(u2);
}
if (method_parameters_length > 0) {
extra_bytes += sizeof(u2);
extra_bytes += method_parameters_length * sizeof(MethodParametersElement);
}
int extra_words = align_size_up(extra_bytes, BytesPerWord) / BytesPerWord;
return align_object_size(header_size() + extra_words);
}
......@@ -143,6 +153,18 @@ u2* ConstMethod::generic_signature_index_addr() const {
u2* ConstMethod::checked_exceptions_length_addr() const {
// Located immediately before the generic signature index.
assert(has_checked_exceptions(), "called only if table is present");
if(has_method_parameters()) {
// If method parameters present, locate immediately before them.
return (u2*)method_parameters_start() - 1;
} else {
// Else, the exception table is at the end of the constMethod.
return has_generic_signature() ? (last_u2_element() - 1) :
last_u2_element();
}
}
u2* ConstMethod::method_parameters_length_addr() const {
assert(has_method_parameters(), "called only if table is present");
return has_generic_signature() ? (last_u2_element() - 1) :
last_u2_element();
}
......@@ -153,11 +175,15 @@ u2* ConstMethod::exception_table_length_addr() const {
// If checked_exception present, locate immediately before them.
return (u2*) checked_exceptions_start() - 1;
} else {
// Else, the exception table is at the end of the constMethod or
// immediately before the generic signature index.
if(has_method_parameters()) {
// If method parameters present, locate immediately before them.
return (u2*)method_parameters_start() - 1;
} else {
// Else, the exception table is at the end of the constMethod.
return has_generic_signature() ? (last_u2_element() - 1) :
last_u2_element();
}
}
}
u2* ConstMethod::localvariable_table_length_addr() const {
......@@ -170,12 +196,16 @@ u2* ConstMethod::localvariable_table_length_addr() const {
// If checked_exception present, locate immediately before them.
return (u2*) checked_exceptions_start() - 1;
} else {
// Else, the linenumber table is at the end of the constMethod or
// immediately before the generic signature index.
if(has_method_parameters()) {
// If method parameters present, locate immediately before them.
return (u2*)method_parameters_start() - 1;
} else {
// Else, the exception table is at the end of the constMethod.
return has_generic_signature() ? (last_u2_element() - 1) :
last_u2_element();
}
}
}
}
// Update the flags to indicate the presence of these optional fields.
......@@ -183,29 +213,57 @@ void ConstMethod::set_inlined_tables_length(u2 generic_signature_index,
int checked_exceptions_len,
int compressed_line_number_size,
int localvariable_table_len,
int exception_table_len) {
// Must be done in the order below, otherwise length_addr accessors
// will not work. Only set bit in header if length is positive.
int exception_table_len,
int method_parameters_len) {
assert(_flags == 0, "Error");
if (compressed_line_number_size > 0) {
if (compressed_line_number_size > 0)
_flags |= _has_linenumber_table;
}
if (generic_signature_index != 0) {
if (generic_signature_index != 0)
_flags |= _has_generic_signature;
*(generic_signature_index_addr()) = generic_signature_index;
}
if (checked_exceptions_len > 0) {
if (method_parameters_len > 0)
_flags |= _has_method_parameters;
if (checked_exceptions_len > 0)
_flags |= _has_checked_exceptions;
*(checked_exceptions_length_addr()) = checked_exceptions_len;
}
if (exception_table_len > 0) {
if (exception_table_len > 0)
_flags |= _has_exception_table;
*(exception_table_length_addr()) = exception_table_len;
}
if (localvariable_table_len > 0) {
if (localvariable_table_len > 0)
_flags |= _has_localvariable_table;
// This code is extremely brittle and should possibly be revised.
// The *_length_addr functions walk backwards through the
// constMethod data, using each of the length indexes ahead of them,
// as well as the flags variable. Therefore, the indexes must be
// initialized in reverse order, or else they will compute the wrong
// offsets. Moving the initialization of _flags into a separate
// block solves *half* of the problem, but the following part will
// still break if the order is not exactly right.
//
// Also, the servicability agent needs to be informed anytime
// anything is added here. It might be advisable to have some sort
// of indication of this inline.
if (generic_signature_index != 0)
*(generic_signature_index_addr()) = generic_signature_index;
// New data should probably go here.
if (method_parameters_len > 0)
*(method_parameters_length_addr()) = method_parameters_len;
if (checked_exceptions_len > 0)
*(checked_exceptions_length_addr()) = checked_exceptions_len;
if (exception_table_len > 0)
*(exception_table_length_addr()) = exception_table_len;
if (localvariable_table_len > 0)
*(localvariable_table_length_addr()) = localvariable_table_len;
}
}
int ConstMethod::method_parameters_length() const {
return has_method_parameters() ? *(method_parameters_length_addr()) : 0;
}
MethodParametersElement* ConstMethod::method_parameters_start() const {
u2* addr = method_parameters_length_addr();
u2 length = *addr;
assert(length > 0, "should only be called if table is present");
addr -= length * sizeof(MethodParametersElement) / sizeof(u2);
return (MethodParametersElement*) addr;
}
......@@ -298,6 +356,10 @@ void ConstMethod::verify_on(outputStream* st) {
}
guarantee(compressed_table_end <= m_end, "invalid method layout");
// Verify checked exceptions, exception table and local variable tables
if (has_method_parameters()) {
u2* addr = method_parameters_length_addr();
guarantee(*addr > 0 && (address) addr >= compressed_table_end && (address) addr < m_end, "invalid method layout");
}
if (has_checked_exceptions()) {
u2* addr = checked_exceptions_length_addr();
guarantee(*addr > 0 && (address) addr >= compressed_table_end && (address) addr < m_end, "invalid method layout");
......@@ -318,6 +380,8 @@ void ConstMethod::verify_on(outputStream* st) {
uncompressed_table_start = (u2*) exception_table_start();
} else if (has_checked_exceptions()) {
uncompressed_table_start = (u2*) checked_exceptions_start();
} else if (has_method_parameters()) {
uncompressed_table_start = (u2*) method_parameters_start();
} else {
uncompressed_table_start = (u2*) m_end;
}
......
......@@ -77,9 +77,18 @@
// | (access flags bit tells whether table is present) |
// | (indexed from end of ConstMethod*) |
// |------------------------------------------------------|
// | method parameters elements + length (length last) |
// | (length is u2, elements are u2, u4 structures) |
// | (see class MethodParametersElement) |
// | (access flags bit tells whether table is present) |
// | (indexed from end of ConstMethod*) |
// |------------------------------------------------------|
// | generic signature index (u2) |
// | (indexed from start of constMethodOop) |
// |------------------------------------------------------|
//
// IMPORTANT: If anything gets added here, there need to be changes to
// ensure that ServicabilityAgent doesn't get broken as a result!
// Utitily class decribing elements in checked exceptions table inlined in Method*.
......@@ -109,6 +118,13 @@ class ExceptionTableElement VALUE_OBJ_CLASS_SPEC {
u2 catch_type_index;
};
// Utility class describing elements in method parameters
class MethodParametersElement VALUE_OBJ_CLASS_SPEC {
public:
u2 name_cp_index;
u4 flags;
};
class ConstMethod : public MetaspaceObj {
friend class VMStructs;
......@@ -123,7 +139,8 @@ private:
_has_localvariable_table = 4,
_has_exception_table = 8,
_has_generic_signature = 16,
_is_overpass = 32
_has_method_parameters = 32,
_is_overpass = 64
};
// Bit vector of signature
......@@ -160,6 +177,7 @@ private:
int localvariable_table_length,
int exception_table_length,
int checked_exceptions_length,
int method_parameters_length,
u2 generic_signature_index,
MethodType is_overpass,
int size);
......@@ -171,6 +189,7 @@ public:
int localvariable_table_length,
int exception_table_length,
int checked_exceptions_length,
int method_parameters_length,
u2 generic_signature_index,
MethodType mt,
TRAPS);
......@@ -182,7 +201,8 @@ public:
int checked_exceptions_len,
int compressed_line_number_size,
int localvariable_table_len,
int exception_table_len);
int exception_table_len,
int method_parameters_length);
bool has_generic_signature() const
{ return (_flags & _has_generic_signature) != 0; }
......@@ -199,6 +219,9 @@ public:
bool has_exception_handler() const
{ return (_flags & _has_exception_table) != 0; }
bool has_method_parameters() const
{ return (_flags & _has_method_parameters) != 0; }
MethodType method_type() const {
return ((_flags & _is_overpass) == 0) ? NORMAL : OVERPASS;
}
......@@ -284,10 +307,11 @@ public:
// Size needed
static int size(int code_size, int compressed_line_number_size,
int local_variable_table_length,
int exception_table_length,
int checked_exceptions_length,
u2 generic_signature_index);
int local_variable_table_length,
int exception_table_length,
int checked_exceptions_length,
int method_parameters_length,
u2 generic_signature_index);
int size() const { return _constMethod_size;}
void set_constMethod_size(int size) { _constMethod_size = size; }
......@@ -308,6 +332,7 @@ public:
u2* checked_exceptions_length_addr() const;
u2* localvariable_table_length_addr() const;
u2* exception_table_length_addr() const;
u2* method_parameters_length_addr() const;
// checked exceptions
int checked_exceptions_length() const;
......@@ -321,6 +346,10 @@ public:
int exception_table_length() const;
ExceptionTableElement* exception_table_start() const;
// method parameters table
int method_parameters_length() const;
MethodParametersElement* method_parameters_start() const;
// byte codes
void set_code(address code) {
if (code_size() > 0) {
......
......@@ -64,6 +64,7 @@ Method* Method::allocate(ClassLoaderData* loader_data,
int localvariable_table_length,
int exception_table_length,
int checked_exceptions_length,
int method_parameters_length,
u2 generic_signature_index,
ConstMethod::MethodType method_type,
TRAPS) {
......@@ -75,6 +76,7 @@ Method* Method::allocate(ClassLoaderData* loader_data,
localvariable_table_length,
exception_table_length,
checked_exceptions_length,
method_parameters_length,
generic_signature_index,
method_type,
CHECK_NULL);
......@@ -1035,8 +1037,10 @@ methodHandle Method::make_method_handle_intrinsic(vmIntrinsics::ID iid,
methodHandle m;
{
Method* m_oop = Method::allocate(loader_data, 0, accessFlags_from(flags_bits),
0, 0, 0, 0, 0, ConstMethod::NORMAL, CHECK_(empty));
Method* m_oop = Method::allocate(loader_data, 0,
accessFlags_from(flags_bits),
0, 0, 0, 0, 0, 0,
ConstMethod::NORMAL, CHECK_(empty));
m = methodHandle(THREAD, m_oop);
}
m->set_constants(cp());
......@@ -1088,6 +1092,7 @@ methodHandle Method::clone_with_new_data(methodHandle m, u_char* new_code, int n
int checked_exceptions_len = m->checked_exceptions_length();
int localvariable_len = m->localvariable_table_length();
int exception_table_len = m->exception_table_length();
int method_parameters_len = m->method_parameters_length();
ClassLoaderData* loader_data = m->method_holder()->class_loader_data();
Method* newm_oop = Method::allocate(loader_data,
......@@ -1097,6 +1102,7 @@ methodHandle Method::clone_with_new_data(methodHandle m, u_char* new_code, int n
localvariable_len,
exception_table_len,
checked_exceptions_len,
method_parameters_len,
generic_signature_index,
m->method_type(),
CHECK_(methodHandle()));
......
......@@ -160,6 +160,7 @@ class Method : public Metadata {
int localvariable_table_length,
int exception_table_length,
int checked_exceptions_length,
int method_parameters_length,
u2 generic_signature_index,
ConstMethod::MethodType method_type,
TRAPS);
......@@ -480,6 +481,12 @@ class Method : public Metadata {
void print_codes_on(outputStream* st) const PRODUCT_RETURN;
void print_codes_on(int from, int to, outputStream* st) const PRODUCT_RETURN;
// method parameters
int method_parameters_length() const
{ return constMethod()->method_parameters_length(); }
MethodParametersElement* method_parameters_start() const
{ return constMethod()->method_parameters_start(); }
// checked exceptions
int checked_exceptions_length() const
{ return constMethod()->checked_exceptions_length(); }
......
......@@ -1515,7 +1515,7 @@ JVM_ENTRY(jbyteArray, JVM_GetFieldAnnotations(JNIEnv *env, jobject field))
JVM_END
static Method* jvm_get_method_common(jobject method, TRAPS) {
static Method* jvm_get_method_common(jobject method) {
// some of this code was adapted from from jni_FromReflectedMethod
oop reflected = JNIHandles::resolve_non_null(method);
......@@ -1533,8 +1533,7 @@ static Method* jvm_get_method_common(jobject method, TRAPS) {
}
Klass* k = java_lang_Class::as_Klass(mirror);
KlassHandle kh(THREAD, k);
Method* m = InstanceKlass::cast(kh())->method_with_idnum(slot);
Method* m = InstanceKlass::cast(k)->method_with_idnum(slot);
if (m == NULL) {
assert(false, "cannot find method");
return NULL; // robustness
......@@ -1548,7 +1547,7 @@ JVM_ENTRY(jbyteArray, JVM_GetMethodAnnotations(JNIEnv *env, jobject method))
JVMWrapper("JVM_GetMethodAnnotations");
// method is a handle to a java.lang.reflect.Method object
Method* m = jvm_get_method_common(method, CHECK_NULL);
Method* m = jvm_get_method_common(method);
return (jbyteArray) JNIHandles::make_local(env,
Annotations::make_java_array(m->annotations(), THREAD));
JVM_END
......@@ -1558,7 +1557,7 @@ JVM_ENTRY(jbyteArray, JVM_GetMethodDefaultAnnotationValue(JNIEnv *env, jobject m
JVMWrapper("JVM_GetMethodDefaultAnnotationValue");
// method is a handle to a java.lang.reflect.Method object
Method* m = jvm_get_method_common(method, CHECK_NULL);
Method* m = jvm_get_method_common(method);
return (jbyteArray) JNIHandles::make_local(env,
Annotations::make_java_array(m->annotation_default(), THREAD));
JVM_END
......@@ -1568,7 +1567,7 @@ JVM_ENTRY(jbyteArray, JVM_GetMethodParameterAnnotations(JNIEnv *env, jobject met
JVMWrapper("JVM_GetMethodParameterAnnotations");
// method is a handle to a java.lang.reflect.Method object
Method* m = jvm_get_method_common(method, CHECK_NULL);
Method* m = jvm_get_method_common(method);
return (jbyteArray) JNIHandles::make_local(env,
Annotations::make_java_array(m->parameter_annotations(), THREAD));
JVM_END
......@@ -1590,6 +1589,32 @@ JVM_ENTRY(jbyteArray, JVM_GetClassTypeAnnotations(JNIEnv *env, jclass cls))
return NULL;
JVM_END
JVM_ENTRY(jobjectArray, JVM_GetMethodParameters(JNIEnv *env, jobject method))
{
JVMWrapper("JVM_GetMethodParameters");
// method is a handle to a java.lang.reflect.Method object
Method* method_ptr = jvm_get_method_common(method);
methodHandle mh (THREAD, method_ptr);
Handle reflected_method (THREAD, JNIHandles::resolve_non_null(method));
const int num_params = mh->method_parameters_length();
if(0 != num_params) {
objArrayOop result_oop = oopFactory::new_objArray(SystemDictionary::reflect_Parameter_klass(), num_params, CHECK_NULL);
objArrayHandle result (THREAD, result_oop);
for(int i = 0; i < num_params; i++) {
MethodParametersElement* params = mh->method_parameters_start();
Symbol* const sym = mh->constants()->symbol_at(params[i].name_cp_index);
oop param = Reflection::new_parameter(reflected_method, i, sym,
params[i].flags, CHECK_NULL);
result->obj_at_put(i, param);
}
return (jobjectArray)JNIHandles::make_local(env, result());
} else {
return (jobjectArray)NULL;
}
}
JVM_END
// New (JDK 1.4) reflection implementation /////////////////////////////////////
......
......@@ -86,6 +86,8 @@ extern "C" {
#define JVM_INTERFACE_VERSION 4
JNIEXPORT jobjectArray JNICALL
JVM_GetMethodParameters(JNIEnv *env, jobject method);
JNIEXPORT jint JNICALL
JVM_GetInterfaceVersion(void);
......
......@@ -860,6 +860,17 @@ oop Reflection::new_field(fieldDescriptor* fd, bool intern_name, TRAPS) {
return rh();
}
oop Reflection::new_parameter(Handle method, int index, Symbol* sym,
int flags, TRAPS) {
Handle name = java_lang_String::create_from_symbol(sym, CHECK_NULL);
Handle rh = java_lang_reflect_Parameter::create(CHECK_NULL);
java_lang_reflect_Parameter::set_name(rh(), name());
java_lang_reflect_Parameter::set_modifiers(rh(), flags);
java_lang_reflect_Parameter::set_executable(rh(), method());
java_lang_reflect_Parameter::set_index(rh(), index);
return rh();
}
methodHandle Reflection::resolve_interface_call(instanceKlassHandle klass, methodHandle method,
KlassHandle recv_klass, Handle receiver, TRAPS) {
......
......@@ -118,6 +118,10 @@ class Reflection: public AllStatic {
static oop new_constructor(methodHandle method, TRAPS);
// Create a java.lang.reflect.Field object based on a field descriptor
static oop new_field(fieldDescriptor* fd, bool intern_name, TRAPS);
// Create a java.lang.reflect.Parameter object based on a
// MethodParameterElement
static oop new_parameter(Handle method, int index, Symbol* sym,
int flags, TRAPS);
private:
// method resolution for invoke
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册