diff --git a/make/CompileInterimLangtools.gmk b/make/CompileInterimLangtools.gmk index 736dfd70280..a0854914862 100644 --- a/make/CompileInterimLangtools.gmk +++ b/make/CompileInterimLangtools.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 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 @@ -80,6 +80,7 @@ define SetupInterimModule ADD_JAVAC_FLAGS := --module-path $(BUILDTOOLS_OUTPUTDIR)/interim_langtools_modules \ $$(INTERIM_LANGTOOLS_ADD_EXPORTS) \ --patch-module java.base=$(BUILDTOOLS_OUTPUTDIR)/gensrc/java.base.interim \ + --add-exports java.base/jdk.internal=java.compiler.interim \ --add-exports java.base/jdk.internal=jdk.compiler.interim \ -Xlint:-module, \ )) diff --git a/make/hotspot/symbols/symbols-unix b/make/hotspot/symbols/symbols-unix index 825be6aaac5..29efcc5b55f 100644 --- a/make/hotspot/symbols/symbols-unix +++ b/make/hotspot/symbols/symbols-unix @@ -122,6 +122,7 @@ JVM_GetNestMembers JVM_GetPrimitiveArrayElement JVM_GetProperties JVM_GetProtectionDomain +JVM_GetRecordComponents JVM_GetSimpleBinaryName JVM_GetStackAccessControlContext JVM_GetSystemPackage @@ -143,6 +144,7 @@ JVM_IsArrayClass JVM_IsConstructorIx JVM_IsInterface JVM_IsPrimitiveClass +JVM_IsRecord JVM_IsSameClassPackage JVM_IsSupportedJNIVersion JVM_IsThreadAlive diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 13abc4b23bf..7231e9a0b12 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -55,6 +55,7 @@ #include "oops/metadata.hpp" #include "oops/method.inline.hpp" #include "oops/oop.inline.hpp" +#include "oops/recordComponent.hpp" #include "oops/symbol.hpp" #include "prims/jvmtiExport.hpp" #include "prims/jvmtiThreadState.hpp" @@ -3211,6 +3212,173 @@ u2 ClassFileParser::parse_classfile_nest_members_attribute(const ClassFileStream return length; } +// Record { +// u2 attribute_name_index; +// u4 attribute_length; +// u2 components_count; +// component_info components[components_count]; +// } +// component_info { +// u2 name_index; +// u2 descriptor_index +// u2 attributes_count; +// attribute_info_attributes[attributes_count]; +// } +u2 ClassFileParser::parse_classfile_record_attribute(const ClassFileStream* const cfs, + const ConstantPool* cp, + const u1* const record_attribute_start, + TRAPS) { + const u1* const current_mark = cfs->current(); + int components_count = 0; + unsigned int calculate_attr_size = 0; + if (record_attribute_start != NULL) { + cfs->set_current(record_attribute_start); + cfs->guarantee_more(2, CHECK_0); // num of components + components_count = (int)cfs->get_u2_fast(); + calculate_attr_size = 2; + } + + Array* const record_components = + MetadataFactory::new_array(_loader_data, components_count, NULL, CHECK_0); + _record_components = record_components; + + for (int x = 0; x < components_count; x++) { + cfs->guarantee_more(6, CHECK_0); // name_index, descriptor_index, attributes_count + + const u2 name_index = cfs->get_u2_fast(); + check_property(valid_symbol_at(name_index), + "Invalid constant pool index %u for name in Record attribute in class file %s", + name_index, CHECK_0); + const Symbol* const name = cp->symbol_at(name_index); + verify_legal_field_name(name, CHECK_0); + + const u2 descriptor_index = cfs->get_u2_fast(); + check_property(valid_symbol_at(descriptor_index), + "Invalid constant pool index %u for descriptor in Record attribute in class file %s", + descriptor_index, CHECK_0); + const Symbol* const descr = cp->symbol_at(descriptor_index); + verify_legal_field_signature(name, descr, CHECK_0); + + const u2 attributes_count = cfs->get_u2_fast(); + calculate_attr_size += 6; + u2 generic_sig_index = 0; + const u1* runtime_visible_annotations = NULL; + int runtime_visible_annotations_length = 0; + const u1* runtime_invisible_annotations = NULL; + int runtime_invisible_annotations_length = 0; + bool runtime_invisible_annotations_exists = false; + const u1* runtime_visible_type_annotations = NULL; + int runtime_visible_type_annotations_length = 0; + const u1* runtime_invisible_type_annotations = NULL; + int runtime_invisible_type_annotations_length = 0; + bool runtime_invisible_type_annotations_exists = false; + + // Expected attributes for record components are Signature, Runtime(In)VisibleAnnotations, + // and Runtime(In)VisibleTypeAnnotations. Other attributes are ignored. + for (int y = 0; y < attributes_count; y++) { + cfs->guarantee_more(6, CHECK_0); // attribute_name_index, attribute_length + const u2 attribute_name_index = cfs->get_u2_fast(); + const u4 attribute_length = cfs->get_u4_fast(); + calculate_attr_size += 6; + check_property( + valid_symbol_at(attribute_name_index), + "Invalid Record attribute name index %u in class file %s", + attribute_name_index, CHECK_0); + + const Symbol* const attribute_name = cp->symbol_at(attribute_name_index); + if (attribute_name == vmSymbols::tag_signature()) { + if (generic_sig_index != 0) { + classfile_parse_error( + "Multiple Signature attributes for Record component in class file %s", + CHECK_0); + } + if (attribute_length != 2) { + classfile_parse_error( + "Invalid Signature attribute length %u in Record component in class file %s", + attribute_length, CHECK_0); + } + generic_sig_index = parse_generic_signature_attribute(cfs, CHECK_0); + + } else if (attribute_name == vmSymbols::tag_runtime_visible_annotations()) { + if (runtime_visible_annotations != NULL) { + classfile_parse_error( + "Multiple RuntimeVisibleAnnotations attributes for Record component in class file %s", CHECK_0); + } + runtime_visible_annotations_length = attribute_length; + runtime_visible_annotations = cfs->current(); + + assert(runtime_visible_annotations != NULL, "null record component visible annotation"); + cfs->guarantee_more(runtime_visible_annotations_length, CHECK_0); + cfs->skip_u1_fast(runtime_visible_annotations_length); + + } else if (attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { + if (runtime_invisible_annotations_exists) { + classfile_parse_error( + "Multiple RuntimeInvisibleAnnotations attributes for Record component in class file %s", CHECK_0); + } + runtime_invisible_annotations_exists = true; + if (PreserveAllAnnotations) { + runtime_invisible_annotations_length = attribute_length; + runtime_invisible_annotations = cfs->current(); + assert(runtime_invisible_annotations != NULL, "null record component invisible annotation"); + } + cfs->skip_u1(attribute_length, CHECK_0); + + } else if (attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) { + if (runtime_visible_type_annotations != NULL) { + classfile_parse_error( + "Multiple RuntimeVisibleTypeAnnotations attributes for Record component in class file %s", CHECK_0); + } + runtime_visible_type_annotations_length = attribute_length; + runtime_visible_type_annotations = cfs->current(); + + assert(runtime_visible_type_annotations != NULL, "null record component visible type annotation"); + cfs->guarantee_more(runtime_visible_type_annotations_length, CHECK_0); + cfs->skip_u1_fast(runtime_visible_type_annotations_length); + + } else if (attribute_name == vmSymbols::tag_runtime_invisible_type_annotations()) { + if (runtime_invisible_type_annotations_exists) { + classfile_parse_error( + "Multiple RuntimeInvisibleTypeAnnotations attributes for Record component in class file %s", CHECK_0); + } + runtime_invisible_type_annotations_exists = true; + if (PreserveAllAnnotations) { + runtime_invisible_type_annotations_length = attribute_length; + runtime_invisible_type_annotations = cfs->current(); + assert(runtime_invisible_type_annotations != NULL, "null record component invisible type annotation"); + } + cfs->skip_u1(attribute_length, CHECK_0); + + } else { + // Skip unknown attributes + cfs->skip_u1(attribute_length, CHECK_0); + } + calculate_attr_size += attribute_length; + } // End of attributes For loop + + AnnotationArray* annotations = assemble_annotations(runtime_visible_annotations, + runtime_visible_annotations_length, + runtime_invisible_annotations, + runtime_invisible_annotations_length, + CHECK_0); + AnnotationArray* type_annotations = assemble_annotations(runtime_visible_type_annotations, + runtime_visible_type_annotations_length, + runtime_invisible_type_annotations, + runtime_invisible_type_annotations_length, + CHECK_0); + + RecordComponent* record_component = + RecordComponent::allocate(_loader_data, name_index, descriptor_index, + attributes_count, generic_sig_index, + annotations, type_annotations, CHECK_0); + record_components->at_put(x, record_component); + } // End of component processing loop + + // Restore buffer's current position. + cfs->set_current(current_mark); + return calculate_attr_size; +} + void ClassFileParser::parse_classfile_synthetic_attribute(TRAPS) { set_class_synthetic_flag(true); } @@ -3308,6 +3476,12 @@ void ClassFileParser::parse_classfile_bootstrap_methods_attribute(const ClassFil CHECK); } +bool ClassFileParser::supports_records() { + return _major_version == JAVA_14_VERSION && + _minor_version == JAVA_PREVIEW_MINOR_VERSION && + Arguments::enable_preview(); +} + void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cfs, ConstantPool* cp, ClassFileParser::ClassAnnotationCollector* parsed_annotations, @@ -3326,6 +3500,7 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf 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; @@ -3345,6 +3520,8 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf 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--) { @@ -3527,6 +3704,38 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf "Nest-host class_info_index %u has bad constant type in class file %s", class_info_index, CHECK); _nest_host = class_info_index; + } else if (_major_version >= JAVA_14_VERSION) { + if (tag == vmSymbols::tag_record()) { + // Skip over Record attribute if not supported or if super class is + // not java.lang.Record. + if (supports_records() && + cp->klass_name_at(_super_class_index) == vmSymbols::java_lang_Record()) { + if (parsed_record_attribute) { + classfile_parse_error("Multiple Record attributes in class file %s", CHECK); + } + // Check that class is final and not abstract. + if (!_access_flags.is_final() || _access_flags.is_abstract()) { + classfile_parse_error("Record attribute in non-final or abstract class file %s", CHECK); + } + parsed_record_attribute = true; + record_attribute_start = cfs->current(); + record_attribute_length = attribute_length; + } else if (log_is_enabled(Info, class, record)) { + // Log why the Record attribute was ignored. Note that if the + // class file version is 58.65535 and --enable-preview wasn't + // specified then a java.lang.UnsupportedClassVersionError + // exception would have been thrown. + ResourceMark rm(THREAD); + log_info(class, record)("Ignoring Record attribute in class %s because %s", + _class_name->as_C_string(), + supports_records() ? "super type is not java.lang.Record" : + "class file version is not 58.65535"); + } + cfs->skip_u1(attribute_length, CHECK); + } else { + // Unknown attribute + cfs->skip_u1(attribute_length, CHECK); + } } else { // Unknown attribute cfs->skip_u1(attribute_length, CHECK); @@ -3578,6 +3787,19 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf } } + if (parsed_record_attribute) { + const unsigned int calculated_attr_length = parse_classfile_record_attribute( + cfs, + cp, + record_attribute_start, + CHECK); + if (_need_verify) { + guarantee_property(record_attribute_length == calculated_attr_length, + "Record attribute has wrong length in class file %s", + CHECK); + } + } + if (_max_bootstrap_specifier_index >= 0) { guarantee_property(parsed_bootstrap_methods_attribute, "Missing BootstrapMethods attribute in class file %s", CHECK); @@ -3632,7 +3854,8 @@ void ClassFileParser::create_combined_annotations(TRAPS) { // 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, + TRAPS) { assert(this_klass != NULL, "invariant"); _cp->set_pool_holder(this_klass); @@ -3644,6 +3867,7 @@ void ClassFileParser::apply_parsed_class_metadata( 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_components(_record_components); // 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 @@ -4514,6 +4738,7 @@ static Array* compute_transitive_interfaces(const InstanceKlass* static void check_super_class_access(const InstanceKlass* this_klass, TRAPS) { assert(this_klass != NULL, "invariant"); const Klass* const super = this_klass->super(); + if (super != NULL) { // If the loader is not the boot loader then throw an exception if its @@ -4715,12 +4940,13 @@ void ClassFileParser::verify_legal_class_modifiers(jint flags, TRAPS) const { const bool is_super = (flags & JVM_ACC_SUPER) != 0; const bool is_enum = (flags & JVM_ACC_ENUM) != 0; const bool is_annotation = (flags & JVM_ACC_ANNOTATION) != 0; - const bool major_gte_15 = _major_version >= JAVA_1_5_VERSION; + const bool major_gte_1_5 = _major_version >= JAVA_1_5_VERSION; + const bool major_gte_14 = _major_version >= JAVA_14_VERSION; if ((is_abstract && is_final) || (is_interface && !is_abstract) || - (is_interface && major_gte_15 && (is_super || is_enum)) || - (!is_interface && major_gte_15 && is_annotation)) { + (is_interface && major_gte_1_5 && (is_super || is_enum)) || + (!is_interface && major_gte_1_5 && is_annotation)) { ResourceMark rm(THREAD); Exceptions::fthrow( THREAD_AND_LOCATION, @@ -4816,14 +5042,14 @@ void ClassFileParser::verify_legal_field_modifiers(jint flags, const bool is_volatile = (flags & JVM_ACC_VOLATILE) != 0; const bool is_transient = (flags & JVM_ACC_TRANSIENT) != 0; const bool is_enum = (flags & JVM_ACC_ENUM) != 0; - const bool major_gte_15 = _major_version >= JAVA_1_5_VERSION; + const bool major_gte_1_5 = _major_version >= JAVA_1_5_VERSION; bool is_illegal = false; if (is_interface) { if (!is_public || !is_static || !is_final || is_private || is_protected || is_volatile || is_transient || - (major_gte_15 && is_enum)) { + (major_gte_1_5 && is_enum)) { is_illegal = true; } } else { // not interface @@ -4859,7 +5085,7 @@ void ClassFileParser::verify_legal_method_modifiers(jint flags, const bool is_strict = (flags & JVM_ACC_STRICT) != 0; const bool is_synchronized = (flags & JVM_ACC_SYNCHRONIZED) != 0; const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0; - const bool major_gte_15 = _major_version >= JAVA_1_5_VERSION; + const bool major_gte_1_5 = _major_version >= JAVA_1_5_VERSION; const bool major_gte_8 = _major_version >= JAVA_8_VERSION; const bool is_initializer = (name == vmSymbols::object_initializer_name()); @@ -4882,7 +5108,7 @@ void ClassFileParser::verify_legal_method_modifiers(jint flags, (is_abstract && (is_private || is_static || is_strict))) { is_illegal = true; } - } else if (major_gte_15) { + } else if (major_gte_1_5) { // Class file version in the interval [JAVA_1_5_VERSION, JAVA_8_VERSION) if (!is_public || is_private || is_protected || is_static || is_final || is_synchronized || is_native || !is_abstract || is_strict) { @@ -4900,13 +5126,13 @@ void ClassFileParser::verify_legal_method_modifiers(jint flags, } else { if (is_initializer) { if (is_static || is_final || is_synchronized || is_native || - is_abstract || (major_gte_15 && is_bridge)) { + is_abstract || (major_gte_1_5 && is_bridge)) { is_illegal = true; } } else { // not initializer if (is_abstract) { if ((is_final || is_native || is_private || is_static || - (major_gte_15 && (is_synchronized || is_strict)))) { + (major_gte_1_5 && (is_synchronized || is_strict)))) { is_illegal = true; } } @@ -5495,6 +5721,7 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loa assert(NULL == _nest_members, "invariant"); assert(NULL == _local_interfaces, "invariant"); assert(NULL == _combined_annotations, "invariant"); + assert(NULL == _record_components, "invariant"); if (_has_final_method) { ik->set_has_final_method(); @@ -5776,6 +6003,7 @@ ClassFileParser::ClassFileParser(ClassFileStream* stream, _inner_classes(NULL), _nest_members(NULL), _nest_host(0), + _record_components(NULL), _local_interfaces(NULL), _transitive_interfaces(NULL), _combined_annotations(NULL), @@ -5886,6 +6114,7 @@ void ClassFileParser::clear_class_metadata() { _combined_annotations = NULL; _class_annotations = _class_type_annotations = NULL; _fields_annotations = _fields_type_annotations = NULL; + _record_components = NULL; } // Destructor to clean up @@ -5913,6 +6142,10 @@ ClassFileParser::~ClassFileParser() { MetadataFactory::free_array(_loader_data, _nest_members); } + if (_record_components != NULL) { + InstanceKlass::deallocate_record_components(_loader_data, _record_components); + } + // Free interfaces InstanceKlass::deallocate_interfaces(_loader_data, _super_klass, _local_interfaces, _transitive_interfaces); diff --git a/src/hotspot/share/classfile/classFileParser.hpp b/src/hotspot/share/classfile/classFileParser.hpp index 440c8396fa3..d8cd9a17747 100644 --- a/src/hotspot/share/classfile/classFileParser.hpp +++ b/src/hotspot/share/classfile/classFileParser.hpp @@ -42,6 +42,7 @@ class FieldInfo; template class GrowableArray; class InstanceKlass; +class RecordComponent; class Symbol; class TempNewSymbol; @@ -98,6 +99,7 @@ class ClassFileParser { Array* _inner_classes; Array* _nest_members; u2 _nest_host; + Array* _record_components; Array* _local_interfaces; Array* _transitive_interfaces; Annotations* _combined_annotations; @@ -287,6 +289,13 @@ class ClassFileParser { const u1* const nest_members_attribute_start, TRAPS); + u2 parse_classfile_record_attribute(const ClassFileStream* const cfs, + const ConstantPool* cp, + const u1* const record_attribute_start, + TRAPS); + + bool supports_records(); + void parse_classfile_attributes(const ClassFileStream* const cfs, ConstantPool* cp, ClassAnnotationCollector* parsed_annotations, diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index fbacc81d9ec..99ce25575fc 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -50,6 +50,7 @@ #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" #include "oops/symbol.hpp" +#include "oops/recordComponent.hpp" #include "oops/typeArrayOop.inline.hpp" #include "prims/jvmtiExport.hpp" #include "prims/resolvedMethodTable.hpp" @@ -3148,6 +3149,64 @@ void java_lang_reflect_Field::set_annotations(oop field, oop value) { field->obj_field_put(annotations_offset, value); } +oop java_lang_reflect_RecordComponent::create(InstanceKlass* holder, RecordComponent* component, TRAPS) { + // Allocate java.lang.reflect.RecordComponent instance + HandleMark hm(THREAD); + InstanceKlass* ik = SystemDictionary::RecordComponent_klass(); + assert(ik != NULL, "must be loaded"); + ik->initialize(CHECK_NULL); + + Handle element = ik->allocate_instance_handle(CHECK_NULL); + + Handle decl_class(THREAD, holder->java_mirror()); + java_lang_reflect_RecordComponent::set_clazz(element(), decl_class()); + + Symbol* name = holder->constants()->symbol_at(component->name_index()); // name_index is a utf8 + oop component_name = StringTable::intern(name, CHECK_NULL); + java_lang_reflect_RecordComponent::set_name(element(), component_name); + + Symbol* type = holder->constants()->symbol_at(component->descriptor_index()); + Handle component_type_h = + SystemDictionary::find_java_mirror_for_type(type, holder, SignatureStream::NCDFError, CHECK_NULL); + java_lang_reflect_RecordComponent::set_type(element(), component_type_h()); + + Method* accessor_method = NULL; + { + // Prepend "()" to type to create the full method signature. + ResourceMark rm(THREAD); + int sig_len = type->utf8_length() + 3; // "()" and null char + char* sig = NEW_RESOURCE_ARRAY(char, sig_len); + jio_snprintf(sig, sig_len, "%c%c%s", JVM_SIGNATURE_FUNC, JVM_SIGNATURE_ENDFUNC, type->as_C_string()); + TempNewSymbol full_sig = SymbolTable::new_symbol(sig); + accessor_method = holder->find_instance_method(name, full_sig); + } + + if (accessor_method != NULL) { + methodHandle method(THREAD, accessor_method); + oop m = Reflection::new_method(method, false, CHECK_NULL); + java_lang_reflect_RecordComponent::set_accessor(element(), m); + } else { + java_lang_reflect_RecordComponent::set_accessor(element(), NULL); + } + + int sig_index = component->generic_signature_index(); + if (sig_index > 0) { + Symbol* sig = holder->constants()->symbol_at(sig_index); // sig_index is a utf8 + oop component_sig = StringTable::intern(sig, CHECK_NULL); + java_lang_reflect_RecordComponent::set_signature(element(), component_sig); + } else { + java_lang_reflect_RecordComponent::set_signature(element(), NULL); + } + + typeArrayOop annotation_oop = Annotations::make_java_array(component->annotations(), CHECK_NULL); + java_lang_reflect_RecordComponent::set_annotations(element(), annotation_oop); + + typeArrayOop type_annotation_oop = Annotations::make_java_array(component->type_annotations(), CHECK_NULL); + java_lang_reflect_RecordComponent::set_typeAnnotations(element(), type_annotation_oop); + + return element(); +} + #define CONSTANTPOOL_FIELDS_DO(macro) \ macro(_oop_offset, k, "constantPoolOop", object_signature, false) @@ -4311,6 +4370,13 @@ int java_lang_Short_ShortCache::_static_cache_offset; int java_lang_Byte_ByteCache::_static_cache_offset; int java_lang_Boolean::_static_TRUE_offset; int java_lang_Boolean::_static_FALSE_offset; +int java_lang_reflect_RecordComponent::clazz_offset; +int java_lang_reflect_RecordComponent::name_offset; +int java_lang_reflect_RecordComponent::type_offset; +int java_lang_reflect_RecordComponent::accessor_offset; +int java_lang_reflect_RecordComponent::signature_offset; +int java_lang_reflect_RecordComponent::annotations_offset; +int java_lang_reflect_RecordComponent::typeAnnotations_offset; @@ -4662,6 +4728,55 @@ static int member_offset(int hardcoded_offset) { return (hardcoded_offset * heapOopSize) + instanceOopDesc::base_offset_in_bytes(); } +#define RECORDCOMPONENT_FIELDS_DO(macro) \ + macro(clazz_offset, k, "clazz", class_signature, false); \ + macro(name_offset, k, "name", string_signature, false); \ + macro(type_offset, k, "type", class_signature, false); \ + macro(accessor_offset, k, "accessor", reflect_method_signature, false); \ + macro(signature_offset, k, "signature", string_signature, false); \ + macro(annotations_offset, k, "annotations", byte_array_signature, false); \ + macro(typeAnnotations_offset, k, "typeAnnotations", byte_array_signature, false); + +// Support for java_lang_reflect_RecordComponent +void java_lang_reflect_RecordComponent::compute_offsets() { + InstanceKlass* k = SystemDictionary::RecordComponent_klass(); + RECORDCOMPONENT_FIELDS_DO(FIELD_COMPUTE_OFFSET); +} + +#if INCLUDE_CDS +void java_lang_reflect_RecordComponent::serialize_offsets(SerializeClosure* f) { + RECORDCOMPONENT_FIELDS_DO(FIELD_SERIALIZE_OFFSET); +} +#endif + +void java_lang_reflect_RecordComponent::set_clazz(oop element, oop value) { + element->obj_field_put(clazz_offset, value); +} + +void java_lang_reflect_RecordComponent::set_name(oop element, oop value) { + element->obj_field_put(name_offset, value); +} + +void java_lang_reflect_RecordComponent::set_type(oop element, oop value) { + element->obj_field_put(type_offset, value); +} + +void java_lang_reflect_RecordComponent::set_accessor(oop element, oop value) { + element->obj_field_put(accessor_offset, value); +} + +void java_lang_reflect_RecordComponent::set_signature(oop element, oop value) { + element->obj_field_put(signature_offset, value); +} + +void java_lang_reflect_RecordComponent::set_annotations(oop element, oop value) { + element->obj_field_put(annotations_offset, value); +} + +void java_lang_reflect_RecordComponent::set_typeAnnotations(oop element, oop value) { + element->obj_field_put(typeAnnotations_offset, value); +} + // Compute hard-coded offsets // Invoked before SystemDictionary::initialize, so pre-loaded classes // are not available to determine the offset_of_static_fields. diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index b1fea1f293a..27c952e82e2 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -30,6 +30,8 @@ #include "oops/oop.hpp" #include "runtime/os.hpp" +class RecordComponent; + // Interface for manipulating the basic Java classes. // // All dependencies on layout of actual Java classes should be kept here. @@ -73,6 +75,7 @@ f(java_lang_reflect_Method) \ f(java_lang_reflect_Constructor) \ f(java_lang_reflect_Field) \ + f(java_lang_reflect_RecordComponent) \ f(java_nio_Buffer) \ f(reflect_ConstantPool) \ f(reflect_UnsafeStaticFieldAccessorImpl) \ @@ -1483,6 +1486,39 @@ class java_lang_LiveStackFrameInfo: AllStatic { friend class JavaClasses; }; +// Interface to java.lang.reflect.RecordComponent objects + +class java_lang_reflect_RecordComponent: AllStatic { + private: + static int clazz_offset; + static int name_offset; + static int type_offset; + static int accessor_offset; + static int signature_offset; + static int annotations_offset; + static int typeAnnotations_offset; + + // Setters + static void set_clazz(oop element, oop value); + static void set_name(oop element, oop value); + static void set_type(oop element, oop value); + static void set_accessor(oop element, oop value); + static void set_signature(oop element, oop value); + static void set_annotations(oop element, oop value); + static void set_typeAnnotations(oop element, oop value); + + public: + // Create an instance of RecordComponent + static oop create(InstanceKlass* holder, RecordComponent* component, TRAPS); + + static void compute_offsets(); + static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN; + + // Debugging + friend class JavaClasses; +}; + + // Interface to java.lang.AssertionStatusDirectives objects class java_lang_AssertionStatusDirectives: AllStatic { diff --git a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp index 52d276d49eb..936a12dc5ee 100644 --- a/src/hotspot/share/classfile/systemDictionary.hpp +++ b/src/hotspot/share/classfile/systemDictionary.hpp @@ -119,6 +119,7 @@ class GCTimer; do_klass(AccessController_klass, java_security_AccessController ) \ do_klass(SecureClassLoader_klass, java_security_SecureClassLoader ) \ do_klass(ClassNotFoundException_klass, java_lang_ClassNotFoundException ) \ + do_klass(Record_klass, java_lang_Record ) \ do_klass(NoClassDefFoundError_klass, java_lang_NoClassDefFoundError ) \ do_klass(LinkageError_klass, java_lang_LinkageError ) \ do_klass(ClassCastException_klass, java_lang_ClassCastException ) \ @@ -217,6 +218,9 @@ class GCTimer; /* force inline of iterators */ \ do_klass(Iterator_klass, java_util_Iterator ) \ \ + /* support for records */ \ + do_klass(RecordComponent_klass, java_lang_reflect_RecordComponent ) \ + \ /*end*/ class SystemDictionary : AllStatic { diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index ee4bfd94742..b23aa86d4fe 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -94,6 +94,7 @@ 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_reflect_RecordComponent, "java/lang/reflect/RecordComponent") \ template(java_lang_StringBuffer, "java/lang/StringBuffer") \ template(java_lang_StringBuilder, "java/lang/StringBuilder") \ template(java_lang_CharSequence, "java/lang/CharSequence") \ @@ -127,6 +128,7 @@ template(jdk_internal_vm_PostVMInitHook, "jdk/internal/vm/PostVMInitHook") \ template(sun_net_www_ParseUtil, "sun/net/www/ParseUtil") \ template(java_util_Iterator, "java/util/Iterator") \ + template(java_lang_Record, "java/lang/Record") \ \ template(jdk_internal_loader_ClassLoaders_AppClassLoader, "jdk/internal/loader/ClassLoaders$AppClassLoader") \ template(jdk_internal_loader_ClassLoaders_PlatformClassLoader, "jdk/internal/loader/ClassLoaders$PlatformClassLoader") \ @@ -161,6 +163,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") \ @@ -562,6 +565,7 @@ template(char_StringBuffer_signature, "(C)Ljava/lang/StringBuffer;") \ template(int_String_signature, "(I)Ljava/lang/String;") \ template(boolean_boolean_int_signature, "(ZZ)I") \ + template(reflect_method_signature, "Ljava/lang/reflect/Method;") \ /* signature symbols needed by intrinsics */ \ VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, template, VM_ALIAS_IGNORE) \ \ diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index 6dbb0c8c31f..0a1c8862689 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -518,6 +518,7 @@ JVM_GetClassDeclaredFields(JNIEnv *env, jclass ofClass, jboolean publicOnly); JNIEXPORT jobjectArray JNICALL JVM_GetClassDeclaredConstructors(JNIEnv *env, jclass ofClass, jboolean publicOnly); + /* 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 @@ -538,6 +539,14 @@ JVM_GetNestHost(JNIEnv *env, jclass current); JNIEXPORT jobjectArray JNICALL JVM_GetNestMembers(JNIEnv *env, jclass current); +/* Records - since JDK 14 */ + +JNIEXPORT jboolean JNICALL +JVM_IsRecord(JNIEnv *env, jclass cls); + +JNIEXPORT jobjectArray JNICALL +JVM_GetRecordComponents(JNIEnv *env, jclass ofClass); + /* The following two reflection routines are still needed due to startup time issues */ /* * java.lang.reflect.Method diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp index e9ce26fae9e..891c05b06d4 100644 --- a/src/hotspot/share/logging/logTag.hpp +++ b/src/hotspot/share/logging/logTag.hpp @@ -137,6 +137,7 @@ LOG_TAG(parser) \ LOG_TAG(ptrqueue) \ LOG_TAG(purge) \ + LOG_TAG(record) \ LOG_TAG(resolve) \ LOG_TAG(safepoint) \ LOG_TAG(sampling) \ diff --git a/src/hotspot/share/memory/allocation.hpp b/src/hotspot/share/memory/allocation.hpp index 9840cba3ed1..b767b0c2491 100644 --- a/src/hotspot/share/memory/allocation.hpp +++ b/src/hotspot/share/memory/allocation.hpp @@ -300,7 +300,8 @@ class MetaspaceObj { f(ConstantPool) \ f(ConstantPoolCache) \ f(Annotations) \ - f(MethodCounters) + f(MethodCounters) \ + f(RecordComponent) #define METASPACE_OBJ_TYPE_DECLARE(name) name ## Type, #define METASPACE_OBJ_TYPE_NAME_CASE(name) case name ## Type: return #name; diff --git a/src/hotspot/share/memory/heapInspection.hpp b/src/hotspot/share/memory/heapInspection.hpp index 4d2d64c8455..4141a0e5da6 100644 --- a/src/hotspot/share/memory/heapInspection.hpp +++ b/src/hotspot/share/memory/heapInspection.hpp @@ -88,6 +88,8 @@ "Number of bytes used by the InstanceKlass::inner_classes() array") \ f(nest_members_bytes, IK_nest_members, \ "Number of bytes used by the InstanceKlass::nest_members() array") \ + f(record_components_bytes, IK_record_components, \ + "Number of bytes used by the InstanceKlass::record_components() array") \ f(signers_bytes, IK_signers, \ "Number of bytes used by the InstanceKlass::singers() array") \ f(class_annotations_bytes, class_annotations, \ diff --git a/src/hotspot/share/memory/metaspaceShared.cpp b/src/hotspot/share/memory/metaspaceShared.cpp index a181a434d50..eb7dfee76e7 100644 --- a/src/hotspot/share/memory/metaspaceShared.cpp +++ b/src/hotspot/share/memory/metaspaceShared.cpp @@ -823,6 +823,7 @@ intptr_t* MetaspaceShared::fix_cpp_vtable_for_dynamic_archive(MetaspaceObj::Type case MetaspaceObj::ConstantPoolCacheType: case MetaspaceObj::AnnotationsType: case MetaspaceObj::MethodCountersType: + case MetaspaceObj::RecordComponentType: // These have no vtables. break; case MetaspaceObj::ClassType: diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 003121e3200..8f1ed169f6a 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -63,6 +63,7 @@ #include "oops/klass.inline.hpp" #include "oops/method.hpp" #include "oops/oop.inline.hpp" +#include "oops/recordComponent.hpp" #include "oops/symbol.hpp" #include "prims/jvmtiExport.hpp" #include "prims/jvmtiRedefineClasses.hpp" @@ -436,6 +437,7 @@ InstanceKlass::InstanceKlass(const ClassFileParser& parser, unsigned kind, Klass _nest_members(NULL), _nest_host_index(0), _nest_host(NULL), + _record_components(NULL), _static_field_size(parser.static_field_size()), _nonstatic_oop_map_size(nonstatic_oop_map_size(parser.total_oop_map_count())), _itable_len(parser.itable_size()), @@ -504,6 +506,17 @@ void InstanceKlass::deallocate_interfaces(ClassLoaderData* loader_data, } } +void InstanceKlass::deallocate_record_components(ClassLoaderData* loader_data, + Array* record_components) { + if (record_components != NULL && !record_components->is_shared()) { + for (int i = 0; i < record_components->length(); i++) { + RecordComponent* record_component = record_components->at(i); + MetadataFactory::free_metadata(loader_data, record_component); + } + MetadataFactory::free_array(loader_data, record_components); + } +} + // This function deallocates the metadata and C heap pointers that the // InstanceKlass points to. void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) { @@ -532,6 +545,9 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) { deallocate_methods(loader_data, methods()); set_methods(NULL); + deallocate_record_components(loader_data, record_components()); + set_record_components(NULL); + if (method_ordering() != NULL && method_ordering() != Universe::the_empty_int_array() && !method_ordering()->is_shared()) { @@ -2339,6 +2355,7 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) { } it->push(&_nest_members); + it->push(&_record_components); } void InstanceKlass::remove_unshareable_info() { @@ -3270,6 +3287,9 @@ void InstanceKlass::print_on(outputStream* st) const { } st->print(BULLET"inner classes: "); inner_classes()->print_value_on(st); st->cr(); st->print(BULLET"nest members: "); nest_members()->print_value_on(st); st->cr(); + if (record_components() != NULL) { + st->print(BULLET"record components: "); record_components()->print_value_on(st); st->cr(); + } if (java_mirror() != NULL) { st->print(BULLET"java mirror: "); java_mirror()->print_value_on(st); @@ -3532,6 +3552,7 @@ void InstanceKlass::collect_statistics(KlassSizeStats *sz) const { n += (sz->_fields_bytes = sz->count_array(fields())); n += (sz->_inner_classes_bytes = sz->count_array(inner_classes())); n += (sz->_nest_members_bytes = sz->count_array(nest_members())); + n += (sz->_record_components_bytes = sz->count_array(record_components())); sz->_ro_bytes += n; const ConstantPool* cp = constants(); @@ -3554,6 +3575,17 @@ void InstanceKlass::collect_statistics(KlassSizeStats *sz) const { } } } + + const Array* components = record_components(); + if (components != NULL) { + for (int i = 0; i < components->length(); i++) { + RecordComponent* component = components->at(i); + if (component != NULL) { + component->collect_statistics(sz); + } + } + } + } #endif // INCLUDE_SERVICES diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 58accb1b1f9..0203ac88601 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -41,6 +41,7 @@ #include "jfr/support/jfrKlassExtension.hpp" #endif +class RecordComponent; // An InstanceKlass is the VM level representation of a Java class. // It contains all information needed for at class at execution runtime. @@ -182,6 +183,9 @@ class InstanceKlass: public Klass { // By always being set it makes nest-member access checks simpler. InstanceKlass* _nest_host; + // The contents of the Record attribute. + Array* _record_components; + // the source debug extension for this klass, NULL if not specified. // Specified as UTF-8 string without terminating zero byte in the classfile, // it is stored in the instanceklass as a NULL-terminated UTF-8 string @@ -448,9 +452,17 @@ class InstanceKlass: public Klass { jushort nest_host_index() const { return _nest_host_index; } void set_nest_host_index(u2 i) { _nest_host_index = i; } + // record components + Array* record_components() const { return _record_components; } + void set_record_components(Array* record_components) { + _record_components = record_components; + } + bool is_record() const { return _record_components != NULL; } + 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; + public: // Returns nest-host class, resolving and validating it if needed // Returns NULL if an exception occurs during loading, or validation fails @@ -1152,6 +1164,8 @@ public: const Klass* super_klass, Array* local_interfaces, Array* transitive_interfaces); + void static deallocate_record_components(ClassLoaderData* loader_data, + Array* record_component); // The constant pool is on stack if any of the methods are executing or // referenced by handles. diff --git a/src/hotspot/share/oops/recordComponent.cpp b/src/hotspot/share/oops/recordComponent.cpp new file mode 100644 index 00000000000..f4c22c237d5 --- /dev/null +++ b/src/hotspot/share/oops/recordComponent.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "logging/log.hpp" +#include "memory/heapInspection.hpp" +#include "memory/metadataFactory.hpp" +#include "memory/metaspace.hpp" +#include "memory/metaspaceClosure.hpp" +#include "oops/annotations.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/recordComponent.hpp" +#include "utilities/globalDefinitions.hpp" + +RecordComponent* RecordComponent::allocate(ClassLoaderData* loader_data, + u2 name_index, u2 descriptor_index, + u2 attributes_count, + u2 generic_signature_index, + AnnotationArray* annotations, + AnnotationArray* type_annotations, TRAPS) { + return new (loader_data, size(), MetaspaceObj::RecordComponentType, THREAD) + RecordComponent(name_index, descriptor_index, attributes_count, + generic_signature_index, annotations, type_annotations); +} + +void RecordComponent::deallocate_contents(ClassLoaderData* loader_data) { + if (annotations() != NULL) { + MetadataFactory::free_array(loader_data, annotations()); + } + if (type_annotations() != NULL) { + MetadataFactory::free_array(loader_data, type_annotations()); + } +} + +void RecordComponent::metaspace_pointers_do(MetaspaceClosure* it) { + log_trace(cds)("Iter(RecordComponent): %p", this); + it->push(&_annotations); + it->push(&_type_annotations); +} + +void RecordComponent::print_value_on(outputStream* st) const { + st->print("RecordComponent(" INTPTR_FORMAT ")", p2i(this)); +} + +#if INCLUDE_SERVICES +void RecordComponent::collect_statistics(KlassSizeStats *sz) const { + if (_annotations != NULL) { + sz->_annotations_bytes += sz->count(_annotations); + sz->_ro_bytes += sz->count(_annotations); + } + if (_type_annotations != NULL) { + sz->_annotations_bytes += sz->count(_type_annotations); + sz->_ro_bytes += sz->count(_type_annotations); + } +} +#endif + +#ifndef PRODUCT +void RecordComponent::print_on(outputStream* st) const { + st->print("name_index: %d", _name_index); + st->print(" - descriptor_index: %d", _descriptor_index); + st->print(" - attributes_count: %d", _attributes_count); + if (_generic_signature_index != 0) { + st->print(" - generic_signature_index: %d", _generic_signature_index); + } + st->cr(); + if (_annotations != NULL) { + st->print_cr("record component annotations"); + _annotations->print_value_on(st); + } + if (_type_annotations != NULL) { + st->print_cr("record component type annotations"); + _type_annotations->print_value_on(st); + } +} +#endif // PRODUCT diff --git a/src/hotspot/share/oops/recordComponent.hpp b/src/hotspot/share/oops/recordComponent.hpp new file mode 100644 index 00000000000..c77cf4aa323 --- /dev/null +++ b/src/hotspot/share/oops/recordComponent.hpp @@ -0,0 +1,107 @@ +/* + * 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. + * + */ + +#ifndef SHARE_OOPS_RECORDCOMPONENT_HPP +#define SHARE_OOPS_RECORDCOMPONENT_HPP + +#include "oops/annotations.hpp" +#include "oops/metadata.hpp" +#include "utilities/globalDefinitions.hpp" + +class KlassSizeStats; + +// This class stores information extracted from the Record class attribute. +class RecordComponent: public MetaspaceObj { + private: + AnnotationArray* _annotations; + AnnotationArray* _type_annotations; + u2 _name_index; + u2 _descriptor_index; + u2 _attributes_count; + + // generic_signature_index gets set if the Record component has a Signature + // attribute. A zero value indicates that there was no Signature attribute. + u2 _generic_signature_index; + + public: + RecordComponent(u2 name_index, u2 descriptor_index, u2 attributes_count, + u2 generic_signature_index, AnnotationArray* annotations, + AnnotationArray* type_annotations): + _annotations(annotations), _type_annotations(type_annotations), + _name_index(name_index), _descriptor_index(descriptor_index), + _attributes_count(attributes_count), + _generic_signature_index(generic_signature_index) { } + + // Allocate instance of this class + static RecordComponent* allocate(ClassLoaderData* loader_data, + u2 name_index, u2 descriptor_index, + u2 attributes_count, + u2 generic_signature_index, + AnnotationArray* annotations, + AnnotationArray* type_annotations, TRAPS); + + void deallocate_contents(ClassLoaderData* loader_data); + + u2 name_index() const { return _name_index; } + void set_name_index(u2 name_index) { _name_index = name_index; } + + u2 descriptor_index() const { return _descriptor_index; } + void set_descriptor_index(u2 descriptor_index) { + _descriptor_index = descriptor_index; + } + + u2 attributes_count() const { return _attributes_count; } + + u2 generic_signature_index() const { return _generic_signature_index; } + void set_generic_signature_index(u2 generic_signature_index) { + _generic_signature_index = generic_signature_index; + } + + AnnotationArray* annotations() const { return _annotations; } + AnnotationArray* type_annotations() const { return _type_annotations; } + + // Size of RecordComponent, not including size of any annotations. + static int size() { return sizeof(RecordComponent) / wordSize; } + + void metaspace_pointers_do(MetaspaceClosure* it); + MetaspaceObj::Type type() const { return RecordComponentType; } + + // Record_components should be stored in the read-only region of CDS archive. + static bool is_read_only_by_default() { return true; } + DEBUG_ONLY(bool on_stack() { return false; }) // for template + +#if INCLUDE_SERVICES + void collect_statistics(KlassSizeStats *sz) const; +#endif + + bool is_klass() const { return false; } + +#ifndef PRODUCT + void print_on(outputStream* st) const; +#endif + void print_value_on(outputStream* st) const; + +}; + +#endif // SHARE_OOPS_RECORDCOMPONENT_HPP diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 89397eb3ff0..baee082eed4 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -51,6 +51,7 @@ #include "oops/fieldStreams.inline.hpp" #include "oops/instanceKlass.hpp" #include "oops/method.hpp" +#include "oops/recordComponent.hpp" #include "oops/objArrayKlass.hpp" #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" @@ -1693,6 +1694,54 @@ JVM_ENTRY(jobjectArray, JVM_GetClassDeclaredFields(JNIEnv *env, jclass ofClass, } JVM_END +JVM_ENTRY(jboolean, JVM_IsRecord(JNIEnv *env, jclass cls)) +{ + JVMWrapper("JVM_IsRecord"); + Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls)); + if (k != NULL && k->is_instance_klass()) { + InstanceKlass* ik = InstanceKlass::cast(k); + return ik->is_record(); + } else { + return false; + } +} +JVM_END + +JVM_ENTRY(jobjectArray, JVM_GetRecordComponents(JNIEnv* env, jclass ofClass)) +{ + JVMWrapper("JVM_GetRecordComponents"); + Klass* c = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(ofClass)); + assert(c->is_instance_klass(), "must be"); + InstanceKlass* ik = InstanceKlass::cast(c); + + if (ik->is_record()) { + Array* components = ik->record_components(); + assert(components != NULL, "components should not be NULL"); + { + JvmtiVMObjectAllocEventCollector oam; + constantPoolHandle cp(THREAD, ik->constants()); + int length = components->length(); + assert(length >= 0, "unexpected record_components length"); + objArrayOop record_components = + oopFactory::new_objArray(SystemDictionary::RecordComponent_klass(), length, CHECK_NULL); + objArrayHandle components_h (THREAD, record_components); + + for (int x = 0; x < length; x++) { + RecordComponent* component = components->at(x); + assert(component != NULL, "unexpected NULL record component"); + oop component_oop = java_lang_reflect_RecordComponent::create(ik, component, CHECK_NULL); + components_h->obj_at_put(x, component_oop); + } + return (jobjectArray)JNIHandles::make_local(components_h()); + } + } + + // Return empty array if ofClass is not a record. + objArrayOop result = oopFactory::new_objArray(SystemDictionary::RecordComponent_klass(), 0, CHECK_NULL); + 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 --git a/src/hotspot/share/prims/jvmti.xml b/src/hotspot/share/prims/jvmti.xml index fb7096714f6..5f90917857c 100644 --- a/src/hotspot/share/prims/jvmti.xml +++ b/src/hotspot/share/prims/jvmti.xml @@ -49,7 +49,7 @@
-agentlib:<agent-lib-name>=<options>
- The name following -agentlib: is the name of the - library to load. Lookup of the library, both its full name and location, - proceeds in a platform-specific manner. - Typically, the <agent-lib-name> is expanded to an - operating system specific file name. - The <options> will be passed to the agent on start-up. - For example, if the option - -agentlib:foo=opt1,opt2 is specified, the VM will attempt to - load the shared library foo.dll from the system PATH + The name following -agentlib: is the name of the + library to load. Lookup of the library, both its full name and location, + proceeds in a platform-specific manner. + Typically, the <agent-lib-name> is expanded to an + operating system specific file name. + The <options> will be passed to the agent on start-up. + For example, if the option + -agentlib:foo=opt1,opt2 is specified, the VM will attempt to + load the shared library foo.dll from the system PATH under Windows or libfoo.so from the - LD_LIBRARY_PATH under the Solaris operating + LD_LIBRARY_PATH under the Solaris operating environment. If the agent library is statically linked into the executable then no actual loading takes place. @@ -505,13 +505,13 @@
-agentpath:<path-to-agent>=<options>
- The path following -agentpath: is the absolute path from which - to load the library. - No library name expansion will occur. - The <options> will be passed to the agent on start-up. - For example, if the option - -agentpath:c:\myLibs\foo.dll=opt1,opt2 is specified, the VM will attempt to - load the shared library c:\myLibs\foo.dll. If the agent + The path following -agentpath: is the absolute path from which + to load the library. + No library name expansion will occur. + The <options> will be passed to the agent on start-up. + For example, if the option + -agentpath:c:\myLibs\foo.dll=opt1,opt2 is specified, the VM will attempt to + load the shared library c:\myLibs\foo.dll. If the agent library is statically linked into the executable then no actual loading takes place.

@@ -574,12 +574,12 @@ Agent_OnLoad_L(JavaVM *vm, char *options, void *reserved) It will be called early enough in VM initialization that:

  • system properties - may be set before they have been used in the start-up of the VM
  • + may be set before they have been used in the start-up of the VM
  • the full set of - capabilities - is still available (note that capabilities that configure the VM - may only be available at this time--see the - Capability function section)
  • + capabilities + is still available (note that capabilities that configure the VM + may only be available at this time--see the + Capability function section)
  • no bytecodes have executed
  • no classes have been loaded
  • no objects have been created
  • @@ -795,29 +795,29 @@ Agent_OnUnload_L(JavaVM *vm) Instrumentation can be inserted in one of three ways:
    • - Static Instrumentation: The class file is instrumented before it - is loaded into the VM - for example, by creating a duplicate directory of - *.class files which have been modified to add the instrumentation. - This method is extremely awkward and, in general, an agent cannot know - the origin of the class files which will be loaded. + Static Instrumentation: The class file is instrumented before it + is loaded into the VM - for example, by creating a duplicate directory of + *.class files which have been modified to add the instrumentation. + This method is extremely awkward and, in general, an agent cannot know + the origin of the class files which will be loaded.
    • - Load-Time Instrumentation: When a class file is loaded by the VM, the raw - bytes of the class file are sent for instrumentation to the agent. - The - event, triggered by the class load, - provides this functionality. This mechanism provides efficient - and complete access to one-time instrumentation. + Load-Time Instrumentation: When a class file is loaded by the VM, the raw + bytes of the class file are sent for instrumentation to the agent. + The + event, triggered by the class load, + provides this functionality. This mechanism provides efficient + and complete access to one-time instrumentation.
    • - Dynamic Instrumentation: A class which is already loaded (and possibly - even running) is modified. This optional feature is provided by the - event, triggered by calling the - function. - Classes can be modified multiple times and can be returned to their - original state. - The mechanism allows instrumentation which changes during the - course of execution. + Dynamic Instrumentation: A class which is already loaded (and possibly + even running) is modified. This optional feature is provided by the + event, triggered by calling the + function. + Classes can be modified multiple times and can be returned to their + original state. + The mechanism allows instrumentation which changes during the + course of execution.

    @@ -1058,49 +1058,49 @@ jvmtiEnv *jvmti; Allocate - Allocate an area of memory through the allocator. + Allocate an area of memory through the allocator. The allocated - memory should be freed with . + memory should be freed with . jvmdi - - - - The number of bytes to allocate. - - jlong is used for compatibility with JVMDI. - - - - - - - On return, a pointer to the beginning of the allocated memory. + + + + The number of bytes to allocate. + + jlong is used for compatibility with JVMDI. + + + + + + + On return, a pointer to the beginning of the allocated memory. If size is zero, NULL is returned. - - + + - - Memory request cannot be honored. - - - is less than zero. - + + Memory request cannot be honored. + + + is less than zero. + Deallocate - Deallocate mem using the allocator. + Deallocate mem using the allocator. This function should - be used to deallocate any memory allocated and returned + be used to deallocate any memory allocated and returned by a function - (including memory allocated with ). + (including memory allocated with ). All allocated memory must be deallocated or the memory cannot be reclaimed. @@ -1108,17 +1108,17 @@ jvmtiEnv *jvmti; - - + + - the call is ignored + the call is ignored - - A pointer to the beginning of the allocated memory. + + A pointer to the beginning of the allocated memory. Please ignore "On return, the elements are set." keep it from generating "On return, the elements are set" - - + + @@ -1209,55 +1209,55 @@ jvmtiEnv *jvmti;

- The answers are represented by the following bit vector. - - - Thread is alive. Zero if thread is new (not started) or terminated. - - - Thread has completed execution. - - - Thread is runnable. - - - Thread is waiting to enter a synchronization block/method or, + The answers are represented by the following bit vector. + + + Thread is alive. Zero if thread is new (not started) or terminated. + + + Thread has completed execution. + + + Thread is runnable. + + + Thread is waiting to enter a synchronization block/method or, after an Object.wait(), waiting to re-enter a synchronization block/method. - - - Thread is waiting. - - - Thread is waiting without a timeout. + + + Thread is waiting. + + + Thread is waiting without a timeout. For example, Object.wait(). - - - Thread is waiting with a maximum time to wait specified. + + + Thread is waiting with a maximum time to wait specified. For example, Object.wait(long). - - - Thread is sleeping -- Thread.sleep(long). - - - Thread is waiting on an object monitor -- Object.wait. - - - Thread is parked, for example: LockSupport.park, + + + Thread is sleeping -- Thread.sleep(long). + + + Thread is waiting on an object monitor -- Object.wait. + + + Thread is parked, for example: LockSupport.park, LockSupport.parkUtil and LockSupport.parkNanos. - - - Thread suspended. - java.lang.Thread.suspend() - or a suspend function + + + Thread suspended. + java.lang.Thread.suspend() + or a suspend function (such as ) has been called on the thread. If this bit - is set, the other bits refer to the thread state before suspension. - - - Thread has been interrupted. - - + is set, the other bits refer to the thread state before suspension. + + + Thread has been interrupted. + + Thread is in native code--that is, a native method is running which has not called back into the VM or Java programming language code. @@ -1266,49 +1266,49 @@ jvmtiEnv *jvmti; language code nor is it set when running VM code or VM support code. Native VM interface functions, such as JNI and functions, may be implemented as VM code. - - + + Defined by VM vendor. - - + + Defined by VM vendor. - - + + Defined by VM vendor. - - + + The following definitions are used to convert thread state to java.lang.Thread.State style states. - - + - Mask the state with this before comparison - - + - java.lang.Thread.State.NEW - - java.lang.Thread.State.NEW + + - java.lang.Thread.State.TERMINATED - - java.lang.Thread.State.TERMINATED + + - java.lang.Thread.State.RUNNABLE - - java.lang.Thread.State.RUNNABLE + + - java.lang.Thread.State.BLOCKED - - java.lang.Thread.State.BLOCKED + + - java.lang.Thread.State.WAITING - - java.lang.Thread.State.WAITING + + - java.lang.Thread.State.TIMED_WAITING - - + java.lang.Thread.State.TIMED_WAITING + + Rules

There can be no more than one answer to a question, although there can be no @@ -1403,26 +1403,26 @@ jvmtiEnv *jvmti; In most cases, the thread state can be determined by testing the one bit corresponding to that question. For example, the code to test if a thread is sleeping: - jint state; - jvmtiError err; + jint state; + jvmtiError err; - err = (*jvmti)->GetThreadState(jvmti, thread, &state); - if (err == JVMTI_ERROR_NONE) { - if (state & JVMTI_THREAD_STATE_SLEEPING) { ... + err = (*jvmti)->GetThreadState(jvmti, thread, &state); + if (err == JVMTI_ERROR_NONE) { + if (state & JVMTI_THREAD_STATE_SLEEPING) { ...

For waiting (that is, in Object.wait, parked, or sleeping) it would be: - if (state & JVMTI_THREAD_STATE_WAITING) { ... + if (state & JVMTI_THREAD_STATE_WAITING) { ... For some states, more than one bit will need to be tested as is the case when testing if a thread has not yet been started: - if ((state & (JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_TERMINATED)) == 0) { ... + if ((state & (JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_TERMINATED)) == 0) { ... To distinguish timed from untimed Object.wait: - if (state & JVMTI_THREAD_STATE_IN_OBJECT_WAIT) { + if (state & JVMTI_THREAD_STATE_IN_OBJECT_WAIT) { if (state & JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT) { printf("in Object.wait(long timeout)\n"); } else { @@ -1440,8 +1440,8 @@ jvmtiEnv *jvmti; by using the provided conversion masks. For example, this returns the name of the java.lang.Thread.State thread state: - err = (*jvmti)->GetThreadState(jvmti, thread, &state); - abortOnError(err); + err = (*jvmti)->GetThreadState(jvmti, thread, &state); + abortOnError(err); switch (state & JVMTI_JAVA_LANG_THREAD_STATE_MASK) { case JVMTI_JAVA_LANG_THREAD_STATE_NEW: return "NEW"; @@ -1462,19 +1462,19 @@ jvmtiEnv *jvmti; - - - - The thread to query. - - - - - - On return, points to state flags, - as defined by the Thread State Flags. - - + + + + The thread to query. + + + + + + On return, points to state flags, + as defined by the Thread State Flags. + + @@ -1499,11 +1499,11 @@ jvmtiEnv *jvmti; - - - On return, points to the current thread, or NULL. - - + + + On return, points to the current thread, or NULL. + + @@ -1528,18 +1528,18 @@ jvmtiEnv *jvmti; - - - On return, points to the number of running threads. - - + + + On return, points to the number of running threads. + + - - - On return, points to an array of references, one - for each running thread. - - + + + On return, points to an array of references, one + for each running thread. + + @@ -1560,11 +1560,11 @@ jvmtiEnv *jvmti; - - - The thread to suspend. - - + + + The thread to suspend. + + @@ -1577,9 +1577,9 @@ jvmtiEnv *jvmti; Suspend All Threads - - There has been no explicit call for this function, and it will - thus be removed if there is no interest. + + There has been no explicit call for this function, and it will + thus be removed if there is no interest. Suspend all live threads except: