Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/hotspot/share/cds/aotConstantPoolResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_signature(ConstantPool* c
}

bool AOTConstantPoolResolver::check_lambda_metafactory_methodtype_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i) {
int mt_index = cp->bsm_attribute_entry(bsms_attribute_index)->argument_index(arg_i);
int mt_index = cp->bsm_attribute_entry(bsms_attribute_index)->argument(arg_i);
if (!cp->tag_at(mt_index).is_method_type()) {
// malformed class?
return false;
Expand All @@ -424,7 +424,7 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_methodtype_arg(ConstantPo
}

bool AOTConstantPoolResolver::check_lambda_metafactory_methodhandle_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i) {
int mh_index = cp->bsm_attribute_entry(bsms_attribute_index)->argument_index(arg_i);
int mh_index = cp->bsm_attribute_entry(bsms_attribute_index)->argument(arg_i);
if (!cp->tag_at(mh_index).is_method_handle()) {
// malformed class?
return false;
Expand Down
76 changes: 31 additions & 45 deletions src/hotspot/share/classfile/classFileParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/annotations.hpp"
#include "oops/bsmAttribute.inline.hpp"
#include "oops/constantPool.inline.hpp"
#include "oops/fieldInfo.hpp"
#include "oops/fieldStreams.inline.hpp"
Expand Down Expand Up @@ -3270,8 +3271,9 @@ void ClassFileParser::parse_classfile_bootstrap_methods_attribute(const ClassFil
TRAPS) {
assert(cfs != nullptr, "invariant");
assert(cp != nullptr, "invariant");
const int cp_size = cp->length();

const u1* const current_start = cfs->current();
const u1* const current_before_parsing = cfs->current();

guarantee_property(attribute_byte_length >= sizeof(u2),
"Invalid BootstrapMethods attribute length %u in class file %s",
Expand All @@ -3280,68 +3282,52 @@ void ClassFileParser::parse_classfile_bootstrap_methods_attribute(const ClassFil

cfs->guarantee_more(attribute_byte_length, CHECK);

const int attribute_array_length = cfs->get_u2_fast();
const int num_bootstrap_methods = cfs->get_u2_fast();

guarantee_property(_max_bootstrap_specifier_index < attribute_array_length,
guarantee_property(_max_bootstrap_specifier_index < num_bootstrap_methods,
"Short length on BootstrapMethods in class file %s",
CHECK);

const u4 bootstrap_methods_u2_len = (attribute_byte_length - sizeof(u2)) / sizeof(u2);

// The attribute contains a counted array of counted tuples of shorts,
// represending bootstrap specifiers:
// length*{bootstrap_method_index, argument_count*{argument_index}}
const unsigned int operand_count = (attribute_byte_length - (unsigned)sizeof(u2)) / (unsigned)sizeof(u2);
// operand_count = number of shorts in attr, except for leading length

// The attribute is copied into a short[] array.
// The array begins with a series of short[2] pairs, one for each tuple.
const int index_size = (attribute_array_length * 2);

Array<u2>* const operands =
MetadataFactory::new_array<u2>(_loader_data, index_size + operand_count, CHECK);

// Eagerly assign operands so they will be deallocated with the constant
// Eagerly assign the arrays so that they will be deallocated with the constant
// pool if there is an error.
cp->set_operands(operands);

int operand_fill_index = index_size;
const int cp_size = cp->length();

for (int n = 0; n < attribute_array_length; n++) {
// Store a 32-bit offset into the header of the operand array.
ConstantPool::operand_offset_at_put(operands, n, operand_fill_index);
BSMAttributeEntries::InsertionIterator iter =
cp->bsm_entries().start_extension(num_bootstrap_methods,
bootstrap_methods_u2_len,
_loader_data,
CHECK);

// Read a bootstrap specifier.
for (int i = 0; i < num_bootstrap_methods; i++) {
cfs->guarantee_more(sizeof(u2) * 2, CHECK); // bsm, argc
const u2 bootstrap_method_index = cfs->get_u2_fast();
const u2 argument_count = cfs->get_u2_fast();
u2 bootstrap_method_ref = cfs->get_u2_fast();
u2 num_bootstrap_arguments = cfs->get_u2_fast();
guarantee_property(
valid_cp_range(bootstrap_method_index, cp_size) &&
cp->tag_at(bootstrap_method_index).is_method_handle(),
"bootstrap_method_index %u has bad constant type in class file %s",
bootstrap_method_index,
CHECK);

guarantee_property((operand_fill_index + 1 + argument_count) < operands->length(),
"Invalid BootstrapMethods num_bootstrap_methods or num_bootstrap_arguments value in class file %s",
CHECK);

operands->at_put(operand_fill_index++, bootstrap_method_index);
operands->at_put(operand_fill_index++, argument_count);

cfs->guarantee_more(sizeof(u2) * argument_count, CHECK); // argv[argc]
for (int j = 0; j < argument_count; j++) {
valid_cp_range(bootstrap_method_ref, cp_size) &&
cp->tag_at(bootstrap_method_ref).is_method_handle(),
"bootstrap_method_index %u has bad constant type in class file %s",
bootstrap_method_ref,
CHECK);
cfs->guarantee_more(sizeof(u2) * num_bootstrap_arguments, CHECK); // argv[argc]

BSMAttributeEntry* entry = iter.reserve_new_entry(bootstrap_method_ref, num_bootstrap_arguments);
guarantee_property(entry != nullptr,
"Invalid BootstrapMethods num_bootstrap_methods."
" The total amount of space reserved for the BootstrapMethod attribute was not sufficient", CHECK);

for (int argi = 0; argi < num_bootstrap_arguments; argi++) {
const u2 argument_index = cfs->get_u2_fast();
guarantee_property(
valid_cp_range(argument_index, cp_size) &&
cp->tag_at(argument_index).is_loadable_constant(),
"argument_index %u has bad constant type in class file %s",
argument_index,
CHECK);
operands->at_put(operand_fill_index++, argument_index);
entry->set_argument(argi, argument_index);
}
}
guarantee_property(current_start + attribute_byte_length == cfs->current(),
cp->bsm_entries().end_extension(iter, _loader_data, CHECK);
guarantee_property(current_before_parsing + attribute_byte_length == cfs->current(),
"Bad length on BootstrapMethods in class file %s",
CHECK);
}
Expand Down
170 changes: 170 additions & 0 deletions src/hotspot/share/oops/bsmAttribute.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
* Copyright (c) 1997, 2025, 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_BSMATTRIBUTE_HPP
#define SHARE_OOPS_BSMATTRIBUTE_HPP

#include "oops/array.hpp"
#include "utilities/checkedCast.hpp"
#include "utilities/globalDefinitions.hpp"

class ClassLoaderData;

class BSMAttributeEntry {
friend class ConstantPool;
friend class BSMAttributeEntries;

u2 _bootstrap_method_index;
u2 _argument_count;

// The argument indexes are stored right after the object, in a contiguous array.
// [ bsmi_0 argc_0 arg_00 arg_01 ... arg_0N bsmi_1 argc_1 arg_10 ... arg_1N ... ]
// So in order to find the argument array, jump over ourselves.
const u2* argument_indexes() const {
return reinterpret_cast<const u2*>(this + 1);
}
u2* argument_indexes() {
return reinterpret_cast<u2*>(this + 1);
}
// These are overlays on top of the BSMAttributeEntries data array, do not construct.
BSMAttributeEntry() = delete;
NONCOPYABLE(BSMAttributeEntry);

void copy_args_into(BSMAttributeEntry* entry) const;

public:
// Offsets for SA
enum {
_bsmi_offset = 0,
_argc_offset = 1,
_argv_offset = 2
};

int bootstrap_method_index() const {
return _bootstrap_method_index;
}
int argument_count() const {
return _argument_count;
}
int argument(int n) const {
assert(checked_cast<u2>(n) < _argument_count, "oob");
return argument_indexes()[n];
}

void set_argument(int index, u2 value) {
assert(index >= 0 && index < argument_count(), "invariant");
argument_indexes()[index] = value;
}

// How many u2s are required to store a BSM entry with argc arguments?
static int u2s_required (u2 argc) {
return 1 /* index */ + 1 /* argc */ + argc /* argv */;
}
};

// The BSMAttributeEntries stores the state of the BootstrapMethods attribute.
class BSMAttributeEntries {
friend class VMStructs;
friend class JVMCIVMStructs;

public:
class InsertionIterator {
friend BSMAttributeEntries;
BSMAttributeEntries* _insert_into;
// Current unused offset into BSMAEs offset array.
int _cur_offset;
// Current unused offset into BSMAEs bsm-data array.
int _cur_array;
public:
InsertionIterator() : _insert_into(nullptr), _cur_offset(-1), _cur_array(-1) {}
InsertionIterator(BSMAttributeEntries* insert_into, int cur_offset, int cur_array)
: _insert_into(insert_into),
_cur_offset(cur_offset),
_cur_array(cur_array) {}
InsertionIterator(const InsertionIterator&) = default;
InsertionIterator& operator=(const InsertionIterator&) = default;

int current_offset() const { return _cur_offset; }
// Add a new BSMAE, reserving the necessary memory for filling the argument vector.
// Returns null if there isn't enough space.
inline BSMAttributeEntry* reserve_new_entry(u2 bsmi, u2 argc);
};

private:
// Each bootstrap method has a variable-sized array associated with it.
// We want constant-time lookup of the Nth BSM. Therefore, we use an offset table,
// such that the Nth BSM is located at _bootstrap_methods[_offsets[N]].
Array<u4>* _offsets;
Array<u2>* _bootstrap_methods;

// Copy the first num_entries into iter.
void copy_into(InsertionIterator& iter, int num_entries) const;

public:
BSMAttributeEntries() : _offsets(nullptr), _bootstrap_methods(nullptr) {}
BSMAttributeEntries(Array<u4>* offsets, Array<u2>* bootstrap_methods)
: _offsets(offsets),
_bootstrap_methods(bootstrap_methods) {}

bool is_empty() const {
return _offsets == nullptr && _bootstrap_methods == nullptr;
}

Array<u4>*& offsets() { return _offsets; }
const Array<u4>* const& offsets() const { return _offsets; }
Array<u2>*& bootstrap_methods() { return _bootstrap_methods; }
const Array<u2>* const& bootstrap_methods() const { return _bootstrap_methods; }

BSMAttributeEntry* entry(int bsms_attribute_index) {
return reinterpret_cast<BSMAttributeEntry*>(_bootstrap_methods->adr_at(_offsets->at(bsms_attribute_index)));
}
const BSMAttributeEntry* entry(int bsms_attribute_index) const {
return reinterpret_cast<BSMAttributeEntry*>(_bootstrap_methods->adr_at(_offsets->at(bsms_attribute_index)));
}

int number_of_entries() const {
return _offsets == nullptr ? 0 : _offsets->length();
}

// The number of U2s the BSM data consists of.
int array_length() const {
return _bootstrap_methods == nullptr ? 0 : _bootstrap_methods->length();
}

void deallocate_contents(ClassLoaderData* loader_data);

// Extend to have the space for both this BSMAEntries and other's.
// Does not copy in the other's BSMAEntrys, that must be done via the InsertionIterator.
// This starts an insertion iterator. Any call to start_extension must have a matching end_extension call.
InsertionIterator start_extension(const BSMAttributeEntries& other, ClassLoaderData* loader_data, TRAPS);
// Extend the BSMAEntries with an additional number_of_entries with a total data_size.
InsertionIterator start_extension(int number_of_entries, int data_size, ClassLoaderData* loader_data, TRAPS);
// Reallocates the underlying memory to fit the limits of the InsertionIterator precisely.
// This ends an insertion iteration. The memory is truncated to fit exactly the data used.
void end_extension(InsertionIterator& iter, ClassLoaderData* loader_data, TRAPS);
// Append all of the BSMAEs in other into this.
void append(const BSMAttributeEntries& other, ClassLoaderData* loader_data, TRAPS);
};

#endif // SHARE_OOPS_BSMATTRIBUTE_HPP
55 changes: 55 additions & 0 deletions src/hotspot/share/oops/bsmAttribute.inline.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2018, 2025, 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_BSMATTRIBUTE_INLINE_HPP
#define SHARE_OOPS_BSMATTRIBUTE_INLINE_HPP

#include "oops/bsmAttribute.hpp"

inline BSMAttributeEntry* BSMAttributeEntries::InsertionIterator::reserve_new_entry(u2 bsmi, u2 argc) {
assert(_insert_into->offsets() != nullptr, "must");
assert(_insert_into->bootstrap_methods() != nullptr, "must");

if (_cur_offset + 1 > _insert_into->offsets()->length() ||
_cur_array + BSMAttributeEntry::u2s_required(argc) > _insert_into->bootstrap_methods()->length()) {
return nullptr;
}
_insert_into->offsets()->at_put(_cur_offset, _cur_array);
BSMAttributeEntry* e = _insert_into->entry(_cur_offset);
e->_bootstrap_method_index = bsmi;
e->_argument_count = argc;

_cur_array += 1 + 1 + argc;
_cur_offset += 1;
return e;
}

inline void BSMAttributeEntry::copy_args_into(BSMAttributeEntry* entry) const {
assert(entry->argument_count() == this->argument_count(), "must be same");
for (int i = 0; i < argument_count(); i++) {
entry->set_argument(i, this->argument(i));
}
}

#endif // SHARE_OOPS_BSMATTRIBUTE_INLINE_HPP
Loading