diff -r 50dc6dd40e6a make/hotspot/symbols/symbols-unix --- a/make/hotspot/symbols/symbols-unix Mon Oct 08 11:14:59 2018 -0700 +++ b/make/hotspot/symbols/symbols-unix Wed Oct 24 17:13:12 2018 -0400 @@ -123,6 +123,7 @@ JVM_GetNestMembers JVM_GetPrimitiveArrayElement JVM_GetProtectionDomain +JVM_GetRecordParameters JVM_GetSimpleBinaryName JVM_GetStackAccessControlContext JVM_GetSystemPackage diff -r 50dc6dd40e6a src/hotspot/share/classfile/classFileParser.cpp --- a/src/hotspot/share/classfile/classFileParser.cpp Mon Oct 08 11:14:59 2018 -0700 +++ b/src/hotspot/share/classfile/classFileParser.cpp Wed Oct 24 17:13:12 2018 -0400 @@ -54,6 +54,7 @@ #include "oops/metadata.hpp" #include "oops/method.hpp" #include "oops/oop.inline.hpp" +#include "oops/recordParamInfo.hpp" #include "oops/symbol.hpp" #include "prims/jvmtiExport.hpp" #include "prims/jvmtiThreadState.hpp" @@ -1742,6 +1743,7 @@ _fields->at_put(i++, fa[j]); } assert(_fields->length() == i, ""); + //tty->print_cr("length of the _fields array %d for class %s", i, _class_name->as_klass_external_name()); } if (_need_verify && length > 1) { @@ -3256,6 +3258,90 @@ return length; } +void ClassFileParser::parse_classfile_record_attribute(const ClassFileStream* const cfs, + const u1* const record_attribute_start, + ConstantPool* cp, + u2* const record_params_count_ptr, + TRAPS) { + assert(NULL == _record_params, "invariant"); + + const u1* const current_mark = cfs->current(); + u2 num_of_params = 0; + if (record_attribute_start != NULL) { + cfs->set_current(record_attribute_start); + cfs->guarantee_more(2, CHECK); // length + num_of_params = cfs->get_u2_fast(); + // DEBUG + // tty->print_cr("this record has %d parameters", num_of_params); + } + + *record_params_count_ptr = num_of_params; + + ResourceMark rm(THREAD); + u2* const record_params_array = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, + u2, + num_of_params * (RecordParamInfo::param_slots + 1)); + for (int n = 0; n < num_of_params; n++) { + cfs->guarantee_more(RecordParamInfo::param_slots, CHECK); + + const u2 name_index = cfs->get_u2_fast(); + check_property(valid_symbol_at(name_index), + "Invalid constant pool index %u for record parameter name in class file %s", + name_index, CHECK); + const Symbol* const name = cp->symbol_at(name_index); + verify_legal_field_name(name, CHECK); + // DEBUG + // tty->print_cr("name read %s", name->as_klass_external_name()); + + AccessFlags access_flags; + const jint flags = cfs->get_u2_fast() & JVM_RECOGNIZED_FIELD_MODIFIERS; + verify_legal_field_modifiers(flags, false, CHECK); + access_flags.set_flags(flags); + + const u2 descriptor_index = cfs->get_u2_fast(); + check_property(valid_symbol_at(descriptor_index), + "Invalid constant pool index %u for record parameter descriptor in class file %s", + descriptor_index, CHECK); + const Symbol* const descriptor = cp->symbol_at(descriptor_index); + verify_legal_field_signature(name, descriptor, CHECK); + // DEBUG + // tty->print_cr("descriptor read %s", descriptor->as_klass_external_name()); + + const u2 signature_index = cfs->get_u2_fast(); + check_property(valid_symbol_at(signature_index), + "Invalid constant pool index %u for record parameter signature in class file %s", + signature_index, CHECK); + const Symbol* const sig = cp->symbol_at(signature_index); + // DEBUG + // tty->print_cr("signature read %s", sig->as_klass_external_name()); + + RecordParamInfo* const record_param_info = RecordParamInfo::from_record_params_array(record_params_array, n); + record_param_info->initialize( + access_flags.as_short(), + name_index, + descriptor_index, + signature_index); + } + + assert(NULL == _record_params, "invariant"); + + _record_params = MetadataFactory::new_array(_loader_data, + num_of_params * RecordParamInfo::param_slots, + CHECK); + { + int i = 0; + for (; i < num_of_params * RecordParamInfo::param_slots; i++) { + _record_params->at_put(i, record_params_array[i]); + } + assert(_record_params->length() == i, ""); + // DEBUG + // tty->print_cr("length of the _record_params array %d for class %s", i, _class_name->as_klass_external_name()); + } + + // Restore buffer's current position. + cfs->set_current(current_mark); +} + void ClassFileParser::parse_classfile_synthetic_attribute(TRAPS) { set_class_synthetic_flag(true); } @@ -3365,12 +3451,15 @@ _inner_classes = Universe::the_empty_short_array(); // Set nest members attribute to default sentinel _nest_members = Universe::the_empty_short_array(); + // Set nest members attribute to default sentinel + _record_params = Universe::the_empty_short_array(); cfs->guarantee_more(2, CHECK); // attributes_count u2 attributes_count = cfs->get_u2_fast(); bool parsed_sourcefile_attribute = false; bool parsed_innerclasses_attribute = false; bool parsed_nest_members_attribute = false; bool parsed_nest_host_attribute = false; + bool parsed_record_attribute = false; bool parsed_enclosingmethod_attribute = false; bool parsed_bootstrap_methods_attribute = false; const u1* runtime_visible_annotations = NULL; @@ -3390,6 +3479,8 @@ u2 enclosing_method_method_index = 0; const u1* nest_members_attribute_start = NULL; u4 nest_members_attribute_length = 0; + const u1* record_attribute_start = NULL; + u4 record_attribute_length = 0; // Iterate over attributes while (attributes_count--) { @@ -3572,9 +3663,15 @@ "Nest-host class_info_index %u has bad constant type in class file %s", class_info_index, CHECK); _nest_host = class_info_index; - } else { - // Unknown attribute - cfs->skip_u1(attribute_length, CHECK); + } else if (tag == vmSymbols::tag_record()) { + if (parsed_record_attribute) { + classfile_parse_error("Multiple Record attributes in class file %s", CHECK); + } else { + parsed_record_attribute = true; + } + record_attribute_start = cfs->current(); + record_attribute_length = attribute_length; + cfs->skip_u1(record_attribute_length, CHECK); } } else { // Unknown attribute @@ -3623,6 +3720,15 @@ } } + if (parsed_record_attribute) { + parse_classfile_record_attribute( + cfs, + record_attribute_start, + cp, + &_record_params_count, + CHECK); + } + if (_max_bootstrap_specifier_index >= 0) { guarantee_property(parsed_bootstrap_methods_attribute, "Missing BootstrapMethods attribute in class file %s", CHECK); @@ -3677,7 +3783,8 @@ // Transfer ownership of metadata allocated to the InstanceKlass. void ClassFileParser::apply_parsed_class_metadata( InstanceKlass* this_klass, - int java_fields_count, TRAPS) { + int java_fields_count, + int record_params_count, TRAPS) { assert(this_klass != NULL, "invariant"); _cp->set_pool_holder(this_klass); @@ -3689,6 +3796,7 @@ this_klass->set_nest_host_index(_nest_host); this_klass->set_local_interfaces(_local_interfaces); this_klass->set_annotations(_combined_annotations); + this_klass->set_record_params(_record_params, record_params_count); // Delay the setting of _transitive_interfaces until after initialize_supers() in // fill_instance_klass(). It is because the _transitive_interfaces may be shared with // its _super. If an OOM occurs while loading the current klass, its _super field @@ -5566,7 +5674,7 @@ // this transfers ownership of a lot of arrays from // the parser onto the InstanceKlass* - apply_parsed_class_metadata(ik, _java_fields_count, CHECK); + apply_parsed_class_metadata(ik, _java_fields_count, _record_params_count, CHECK); // note that is not safe to use the fields in the parser from this point on assert(NULL == _cp, "invariant"); @@ -5576,6 +5684,7 @@ assert(NULL == _nest_members, "invariant"); assert(NULL == _local_interfaces, "invariant"); assert(NULL == _combined_annotations, "invariant"); + assert(NULL == _record_params, "invariant"); if (_has_final_method) { ik->set_has_final_method(); @@ -5845,6 +5954,7 @@ _inner_classes(NULL), _nest_members(NULL), _nest_host(0), + _record_params(NULL), _local_interfaces(NULL), _transitive_interfaces(NULL), _combined_annotations(NULL), @@ -5878,6 +5988,7 @@ _super_class_index(0), _itfs_len(0), _java_fields_count(0), + _record_params_count(0), _need_verify(false), _relax_verify(false), _has_nonstatic_concrete_methods(false), @@ -5954,6 +6065,7 @@ _combined_annotations = NULL; _annotations = _type_annotations = NULL; _fields_annotations = _fields_type_annotations = NULL; + _record_params = NULL; } // Destructor to clean up @@ -5979,6 +6091,10 @@ MetadataFactory::free_array(_loader_data, _nest_members); } + if (_record_params != NULL && _record_params != Universe::the_empty_short_array()) { + MetadataFactory::free_array(_loader_data, _record_params); + } + // Free interfaces InstanceKlass::deallocate_interfaces(_loader_data, _super_klass, _local_interfaces, _transitive_interfaces); diff -r 50dc6dd40e6a src/hotspot/share/classfile/classFileParser.hpp --- a/src/hotspot/share/classfile/classFileParser.hpp Mon Oct 08 11:14:59 2018 -0700 +++ b/src/hotspot/share/classfile/classFileParser.hpp Wed Oct 24 17:13:12 2018 -0400 @@ -99,6 +99,7 @@ Array* _inner_classes; Array* _nest_members; u2 _nest_host; + Array* _record_params; Array* _local_interfaces; Array* _transitive_interfaces; Annotations* _combined_annotations; @@ -153,6 +154,7 @@ u2 _super_class_index; u2 _itfs_len; u2 _java_fields_count; + u2 _record_params_count; bool _need_verify; bool _relax_verify; @@ -188,7 +190,7 @@ void create_combined_annotations(TRAPS); void apply_parsed_class_attributes(InstanceKlass* k); // update k - void apply_parsed_class_metadata(InstanceKlass* k, int fields_count, TRAPS); + void apply_parsed_class_metadata(InstanceKlass* k, int fields_count, int record_params_count, TRAPS); void clear_class_metadata(); // Constant pool parsing @@ -296,6 +298,12 @@ const u1* const nest_members_attribute_start, TRAPS); + void parse_classfile_record_attribute(const ClassFileStream* const cfs, + const u1* const record_attribute_start, + ConstantPool* cp, + u2* const record_params_count_ptr, + TRAPS); + void parse_classfile_attributes(const ClassFileStream* const cfs, ConstantPool* cp, ClassAnnotationCollector* parsed_annotations, diff -r 50dc6dd40e6a src/hotspot/share/classfile/vmSymbols.hpp --- a/src/hotspot/share/classfile/vmSymbols.hpp Mon Oct 08 11:14:59 2018 -0700 +++ b/src/hotspot/share/classfile/vmSymbols.hpp Wed Oct 24 17:13:12 2018 -0400 @@ -156,6 +156,7 @@ template(tag_deprecated, "Deprecated") \ template(tag_source_debug_extension, "SourceDebugExtension") \ template(tag_signature, "Signature") \ + template(tag_record, "Record") \ template(tag_runtime_visible_annotations, "RuntimeVisibleAnnotations") \ template(tag_runtime_invisible_annotations, "RuntimeInvisibleAnnotations") \ template(tag_runtime_visible_parameter_annotations, "RuntimeVisibleParameterAnnotations") \ diff -r 50dc6dd40e6a src/hotspot/share/include/jvm.h --- a/src/hotspot/share/include/jvm.h Mon Oct 08 11:14:59 2018 -0700 +++ b/src/hotspot/share/include/jvm.h Wed Oct 24 17:13:12 2018 -0400 @@ -515,6 +515,9 @@ JNIEXPORT jobjectArray JNICALL JVM_GetClassDeclaredConstructors(JNIEnv *env, jclass ofClass, jboolean publicOnly); +JNIEXPORT jobjectArray JNICALL +JVM_GetRecordParameters(JNIEnv *env, jclass ofClass); + /* Differs from JVM_GetClassModifiers in treatment of inner classes. This returns the access flags for the class as specified in the class file rather than searching the InnerClasses attribute (if diff -r 50dc6dd40e6a src/hotspot/share/oops/instanceKlass.cpp --- a/src/hotspot/share/oops/instanceKlass.cpp Mon Oct 08 11:14:59 2018 -0700 +++ b/src/hotspot/share/oops/instanceKlass.cpp Wed Oct 24 17:13:12 2018 -0400 @@ -564,6 +564,12 @@ } set_nest_members(NULL); + if (record_params() != NULL && + record_params() != Universe::the_empty_short_array()) { + MetadataFactory::free_array(loader_data, record_params()); + } + set_record_params(NULL, 0); + // We should deallocate the Annotations instance if it's not in shared spaces. if (annotations() != NULL && !annotations()->is_shared()) { MetadataFactory::free_metadata(loader_data, annotations()); @@ -2270,6 +2276,7 @@ } it->push(&_nest_members); + it->push(&_record_params); } void InstanceKlass::remove_unshareable_info() { diff -r 50dc6dd40e6a src/hotspot/share/oops/instanceKlass.hpp --- a/src/hotspot/share/oops/instanceKlass.hpp Mon Oct 08 11:14:59 2018 -0700 +++ b/src/hotspot/share/oops/instanceKlass.hpp Wed Oct 24 17:13:12 2018 -0400 @@ -35,6 +35,7 @@ #include "oops/fieldInfo.hpp" #include "oops/instanceOop.hpp" #include "oops/klassVtable.hpp" +#include "oops/recordParamInfo.hpp" #include "runtime/handles.hpp" #include "runtime/os.hpp" #include "utilities/accessFlags.hpp" @@ -188,6 +189,8 @@ // if this class is unloaded. Symbol* _array_name; + Array* _record_params; + // Number of heapOopSize words used by non-static fields in this klass // (including inherited fields but after header_size()). int _nonstatic_field_size; @@ -200,6 +203,7 @@ u2 _source_file_name_index; u2 _static_oop_field_count;// number of static oop fields in this klass u2 _java_fields_count; // The number of declared Java fields + u2 _record_params_count; // The number of record parameters int _nonstatic_oop_map_size;// size in words of nonstatic oop map blocks int _itable_len; // length of Java itable (in words) @@ -430,6 +434,8 @@ friend class fieldDescriptor; FieldInfo* field(int index) const { return FieldInfo::from_field_array(_fields, index); } + RecordParamInfo* record_param(int index) const { return RecordParamInfo::from_record_params_array(_record_params, index); } + public: int field_offset (int index) const { return field(index)->offset(); } int field_access_flags(int index) const { return field(index)->access_flags(); } @@ -458,6 +464,21 @@ jushort nest_host_index() const { return _nest_host_index; } void set_nest_host_index(u2 i) { _nest_host_index = i; } + // record parameters + int record_param_access_flags(int index) const { return record_param(index)->access_flags(); } + Symbol* record_param_name(int index) const { return record_param(index)->name(constants()); } + Symbol* record_param_signature(int index) const { return record_param(index)->signature(constants()); } + Symbol* record_param_descriptor(int index) const { return record_param(index)->signature(constants()); } + + int record_params_count() const { return (int)_record_params_count; } + + Array* record_params() const { return _record_params; } + void set_record_params(Array* record_params, u2 record_params_count) { + guarantee(_record_params == NULL || record_params == NULL, "Just checking"); + _record_params = record_params; + _record_params_count = record_params_count; + } + private: // Called to verify that k is a member of this nest - does not look at k's nest-host bool has_nest_member(InstanceKlass* k, TRAPS) const; diff -r 50dc6dd40e6a src/hotspot/share/oops/recordParamInfo.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/oops/recordParamInfo.hpp Wed Oct 24 17:13:12 2018 -0400 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018, 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. + * + */ + +#ifndef SHARE_VM_OOPS_RECORDPARAMINFO_HPP +#define SHARE_VM_OOPS_RECORDPARAMINFO_HPP + +#include "oops/constantPool.hpp" +#include "oops/typeArrayOop.hpp" +#include "classfile/vmSymbols.hpp" + +// This class represents the parameter information contained in the recordParams +// array of an InstanceKlass. Currently it's laid on top an array of +// Java shorts but in the future it could simply be used as a real +// array type. RecordParamInfo generally shouldn't be used directly. +// Record parameters should be queried through InstanceKlass. + +class RecordParamInfo { + friend class ClassFileParser; + friend class RecordParameterStreamBase; + friend class JavaRecordParameterStream; + enum ParamOffset { + access_flags_offset = 0, + name_index_offset = 1, + descriptor_index_offset = 2, + signature_index_offset = 3, + param_slots = 4 + }; + +private: + u2 _shorts[param_slots]; + + void set_name_index(u2 val) { _shorts[name_index_offset] = val; } + void set_descriptor_index(u2 val) { _shorts[descriptor_index_offset] = val; } + void set_signature_index(u2 val) { _shorts[signature_index_offset] = val; } + + u2 name_index() const { return _shorts[name_index_offset]; } + u2 descriptor_index() const { return _shorts[descriptor_index_offset]; } + u2 signature_index() const { return _shorts[signature_index_offset]; } +public: + static RecordParamInfo* from_record_params_array(Array* record_params, int index) { + return ((RecordParamInfo*)record_params->adr_at(index * param_slots)); + } + static RecordParamInfo* from_record_params_array(u2* record_params, int index) { + return ((RecordParamInfo*)(record_params + index * param_slots)); + } + + void initialize(u2 access_flags, + u2 name_index, + u2 descriptor_index, + u2 signature_index) { + _shorts[access_flags_offset] = access_flags; + _shorts[name_index_offset] = name_index; + _shorts[descriptor_index_offset] = descriptor_index; + _shorts[signature_index_offset] = signature_index; + } + + u2 access_flags() const { return _shorts[access_flags_offset]; } + Symbol* name(const constantPoolHandle& cp) const { return cp->symbol_at(name_index()); } + Symbol* signature(const constantPoolHandle& cp) const { return cp->symbol_at(signature_index()); } + Symbol* descriptor(const constantPoolHandle& cp) const { return cp->symbol_at(descriptor_index()); } + void set_access_flags(u2 val) { _shorts[access_flags_offset] = val; } +}; + +#endif // SHARE_VM_OOPS_RECORDPARAMINFO_HPP diff -r 50dc6dd40e6a src/hotspot/share/oops/recordParamStreams.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/oops/recordParamStreams.hpp Wed Oct 24 17:13:12 2018 -0400 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2018, 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. + * + */ + +#ifndef SHARE_VM_OOPS_RECORDPARAMSTREAMS_HPP +#define SHARE_VM_OOPS_RECORDPARAMSTREAMS_HPP + +#include "oops/instanceKlass.hpp" +#include "oops/recordParamInfo.hpp" + +// The is the base class for iteration over the record parameters array +// describing the parameters in a record. +class RecordParameterStreamBase : public StackObj { + protected: + Array* _record_parameters; + constantPoolHandle _constants; + int _index; + int _limit; + + RecordParamInfo* record_param() const { return RecordParamInfo::from_record_params_array(_record_parameters, _index); } + InstanceKlass* record_param_holder() const { return _constants->pool_holder(); } + + RecordParameterStreamBase(Array* record_params, const constantPoolHandle& constants, int start, int limit) { + _record_parameters = record_params; + _constants = constants; + _index = start; + int num_record_parameters = record_params->length() / RecordParamInfo::param_slots; + if (limit < start) { + _limit = num_record_parameters; + } else { + _limit = limit; + } + } + + RecordParameterStreamBase(Array* record_params, const constantPoolHandle& constants) { + _record_parameters = record_params; + _constants = constants; + _index = 0; + _limit = record_params->length() / RecordParamInfo::param_slots;; + } + + public: + RecordParameterStreamBase(InstanceKlass* klass) { + _record_parameters = klass->record_params(); + _constants = klass->constants(); + _index = 0; + _limit = klass->record_params_count(); + assert(klass == record_param_holder(), ""); + } + + // accessors + int index() const { return _index; } + + void next() { + _index += 1; + } + bool done() const { return _index >= _limit; } + + // Accessors for current record parameter + AccessFlags access_flags() const { + AccessFlags flags; + flags.set_flags(record_param()->access_flags()); + return flags; + } + + void set_access_flags(u2 flags) const { + record_param()->set_access_flags(flags); + } + + void set_access_flags(AccessFlags flags) const { + set_access_flags(flags.as_short()); + } + + Symbol* name() const { + return record_param()->name(_constants); + } + + Symbol* descriptor() const { + return record_param()->descriptor(_constants); + } + + Symbol* signature() const { + return record_param()->signature(_constants); + } +}; + +// Iterate over the record parameters +class JavaRecordParameterStream : public RecordParameterStreamBase { + public: + JavaRecordParameterStream(const InstanceKlass* k): RecordParameterStreamBase(k->record_params(), k->constants(), 0, k->record_params_count()) {} + + int name_index() const { + return record_param()->name_index(); + } + void set_name_index(int index) { + record_param()->set_name_index(index); + } + int descriptor_index() const { + return record_param()->descriptor_index(); + } + void set_descriptor_index(int index) { + record_param()->set_descriptor_index(index); + } + int signature_index() const { + return record_param()->signature_index(); + } + void set_generic_signature_index(int index) { + record_param()->set_signature_index(index); + } +}; + +#endif // SHARE_VM_OOPS_RECORDPARAMSTREAMS_HPP diff -r 50dc6dd40e6a src/hotspot/share/prims/jvm.cpp --- a/src/hotspot/share/prims/jvm.cpp Mon Oct 08 11:14:59 2018 -0700 +++ b/src/hotspot/share/prims/jvm.cpp Wed Oct 24 17:13:12 2018 -0400 @@ -51,6 +51,7 @@ #include "oops/objArrayKlass.hpp" #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" +#include "oops/recordParamStreams.hpp" #include "prims/jvm_misc.hpp" #include "prims/jvmtiExport.hpp" #include "prims/jvmtiThreadState.hpp" @@ -1787,6 +1788,56 @@ } JVM_END +JVM_ENTRY(jobjectArray, JVM_GetRecordParameters(JNIEnv *env, jclass ofClass)) +{ + JVMWrapper("JVM_GetRecordParameters"); + JvmtiVMObjectAllocEventCollector oam; + + // Exclude primitive types and array types + if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(ofClass)) || + java_lang_Class::as_Klass(JNIHandles::resolve_non_null(ofClass))->is_array_klass()) { + // Return empty array + oop res = oopFactory::new_objArray(SystemDictionary::reflect_Field_klass(), 0, CHECK_NULL); + return (jobjectArray) JNIHandles::make_local(env, res); + } + + InstanceKlass* k = InstanceKlass::cast(java_lang_Class::as_Klass(JNIHandles::resolve_non_null(ofClass))); + constantPoolHandle cp(THREAD, k->constants()); + + // Ensure class is linked + k->link_class(CHECK_NULL); + + // Allocate result + int num_record_params = k->record_params_count(); + Array* record_parameters = k->record_params(); + // DEBUG + //tty->print_cr("num_record_params == %d", num_record_params); + + if (num_record_params == 0) { + oop res = oopFactory::new_objArray(SystemDictionary::reflect_Field_klass(), 0, CHECK_NULL); + return (jobjectArray) JNIHandles::make_local(env, res); + } + + objArrayOop r = oopFactory::new_objArray(SystemDictionary::reflect_Field_klass(), num_record_params, CHECK_NULL); + objArrayHandle result (THREAD, r); + + int out_idx = 0; + fieldDescriptor fd; + for (JavaRecordParameterStream recordParamsStream(k); !recordParamsStream.done(); recordParamsStream.next()) { + for (JavaFieldStream fileStream(k); !fileStream.done(); fileStream.next()) { + if (fileStream.name() == recordParamsStream.name()) { + fd.reinitialize(k, fileStream.index()); + oop field = Reflection::new_field(&fd, CHECK_NULL); + result->obj_at_put(out_idx, field); + ++out_idx; + } + } + } + assert(out_idx == num_record_params, "just checking"); + return (jobjectArray) JNIHandles::make_local(env, result()); +} +JVM_END + static bool select_method(const methodHandle& method, bool want_constructor) { if (want_constructor) { return (method->is_initializer() && !method->is_static()); diff -r 50dc6dd40e6a src/java.base/share/classes/java/lang/AbstractRecord.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/java/lang/AbstractRecord.java Wed Oct 24 17:13:12 2018 -0400 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package java.lang; + +import java.lang.annotation.Data; + +/** + * AbstractRecord + * + * @author Brian Goetz + */ +@Data +public abstract class AbstractRecord { + + @Override + public abstract int hashCode(); + + @Override + public abstract boolean equals(Object obj); + + @Override + public abstract String toString(); +} + diff -r 50dc6dd40e6a src/java.base/share/classes/java/lang/Class.java --- a/src/java.base/share/classes/java/lang/Class.java Mon Oct 08 11:14:59 2018 -0700 +++ b/src/java.base/share/classes/java/lang/Class.java Wed Oct 24 17:13:12 2018 -0400 @@ -2236,6 +2236,20 @@ return copyFields(privateGetDeclaredFields(false)); } + /** + * TBD + * @return TBD + * @throws SecurityException TBD + * @since 1.12 + */ + @CallerSensitive + public Field[] getRecordParameters() throws SecurityException { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true); + } + return copyFields(privateGetRecordParameters()); + } /** * Returns an array containing {@code Method} objects reflecting all the @@ -2937,6 +2951,8 @@ volatile Field[] declaredPublicFields; volatile Method[] declaredPublicMethods; volatile Class[] interfaces; + // record parameters + volatile Field[] recordParameters; // Cached names String simpleName; @@ -3057,6 +3073,21 @@ return res; } + private Field[] privateGetRecordParameters() { + Field[] res; + ReflectionData rd = reflectionData(); + if (rd != null) { + res = rd.recordParameters; + if (res != null) return res; + } + // No cached value available; request value from VM + res = Reflection.filterFields(this, getRecordParameters0()); + if (rd != null) { + rd.recordParameters = res; + } + return res; + } + // Returns an array of "root" fields. These Field objects must NOT // be propagated to the outside world, but must instead be copied // via ReflectionFactory.copyField. @@ -3394,6 +3425,7 @@ private native Method[] getDeclaredMethods0(boolean publicOnly); private native Constructor[] getDeclaredConstructors0(boolean publicOnly); private native Class[] getDeclaredClasses0(); + private native Field[] getRecordParameters0(); /** * Helper method to get the method name from arguments. @@ -3492,6 +3524,39 @@ this.getSuperclass() == java.lang.Enum.class; } + /** + * Returns true if and only if this class was declared as a record in the + * source code. + * + * @return true if and only if this class was declared as a record in the + * source code + * @since 1.12 + */ + public boolean isRecord() { + // A record must directly extend java.lang.AbstractRecord + return AbstractRecord.class.isAssignableFrom(this); + } + + /** + * Returns an array with the names of the components + * @return an array with the names of the components + * @since 1.12 + */ + public String[] getRecordComponentNames() { + if (isRecord()) { + Field[] fields = privateGetDeclaredFields(false); + String[] names = new String[fields.length]; + int i = 0; + for (Field field : fields) { + names[i] = field.getName(); + i++; + } + return names; + } else { + return new String[0]; + } + } + // Fetches the factory for reflective objects private static ReflectionFactory getReflectionFactory() { if (reflectionFactory == null) { diff -r 50dc6dd40e6a src/java.base/share/native/libjava/Class.c --- a/src/java.base/share/native/libjava/Class.c Mon Oct 08 11:14:59 2018 -0700 +++ b/src/java.base/share/native/libjava/Class.c Wed Oct 24 17:13:12 2018 -0400 @@ -76,6 +76,7 @@ {"getRawTypeAnnotations", "()" BA, (void *)&JVM_GetClassTypeAnnotations}, {"getNestHost0", "()" CLS, (void *)&JVM_GetNestHost}, {"getNestMembers0", "()[" CLS, (void *)&JVM_GetNestMembers}, + {"getRecordParameters0", "()[" FLD, (void *)&JVM_GetRecordParameters}, }; #undef OBJ