Skip to content

Commit 7a1ff29

Browse files
Matias Saavedra SilvaVicente Romero
authored andcommitted
8343846: [lworld] implement spec changes to stack map tables
Co-authored-by: Vicente Romero <vromero@openjdk.org> Reviewed-by: coleenp, fparain, liach
1 parent ad75b32 commit 7a1ff29

File tree

26 files changed

+1147
-227
lines changed

26 files changed

+1147
-227
lines changed

src/hotspot/share/classfile/stackMapFrame.cpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@
3131
#include "runtime/handles.inline.hpp"
3232
#include "utilities/globalDefinitions.hpp"
3333

34-
StackMapFrame::StackMapFrame(u2 max_locals, u2 max_stack, ClassVerifier* v) :
34+
StackMapFrame::StackMapFrame(u2 max_locals, u2 max_stack, AssertUnsetFieldTable* initial_strict_fields, ClassVerifier* v) :
3535
_offset(0), _locals_size(0), _stack_size(0),
3636
_stack_mark(0), _max_locals(max_locals),
37-
_max_stack(max_stack), _flags(0), _verifier(v) {
37+
_max_stack(max_stack), _flags(0), _assert_unset_fields(initial_strict_fields), _verifier(v) {
3838
Thread* thr = v->thread();
3939
_locals = NEW_RESOURCE_ARRAY_IN_THREAD(thr, VerificationType, max_locals);
4040
_stack = NEW_RESOURCE_ARRAY_IN_THREAD(thr, VerificationType, max_stack);
@@ -47,10 +47,23 @@ StackMapFrame::StackMapFrame(u2 max_locals, u2 max_stack, ClassVerifier* v) :
4747
}
4848
}
4949

50+
void StackMapFrame::print_strict_fields(AssertUnsetFieldTable* table) {
51+
ResourceMark rm;
52+
auto printfields = [&] (const NameAndSig& key, const bool& value) {
53+
log_info(verification)("Strict field: %s%s (Satisfied: %s)",
54+
key._name->as_C_string(),
55+
key._signature->as_C_string(),
56+
value ? "true" : "false");
57+
};
58+
table->iterate_all(printfields);
59+
}
60+
5061
StackMapFrame* StackMapFrame::frame_in_exception_handler(u1 flags) {
5162
Thread* thr = _verifier->thread();
5263
VerificationType* stack = NEW_RESOURCE_ARRAY_IN_THREAD(thr, VerificationType, 1);
53-
StackMapFrame* frame = new StackMapFrame(_offset, flags, _locals_size, 0, _max_locals, _max_stack, _locals, stack, _verifier);
64+
StackMapFrame* frame = new StackMapFrame(_offset, flags, _locals_size, 0,
65+
_max_locals, _max_stack, _locals, stack,
66+
_assert_unset_fields, _verifier);
5467
return frame;
5568
}
5669

@@ -188,6 +201,14 @@ bool StackMapFrame::is_assignable_to(
188201
return false;
189202
}
190203

204+
// Check that assert unset fields are compatible
205+
bool compatible = verify_unset_fields_compatibility(target->assert_unset_fields());
206+
if (!compatible) {
207+
*ctx = ErrorContext::strict_fields_mismatch(target->offset(),
208+
(StackMapFrame*)this, (StackMapFrame*)target);
209+
return false;
210+
}
211+
191212
if ((_flags | target->flags()) == target->flags()) {
192213
return true;
193214
} else {

src/hotspot/share/classfile/stackMapFrame.hpp

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -41,6 +41,21 @@ enum {
4141
};
4242

4343
class StackMapFrame : public ResourceObj {
44+
public:
45+
static unsigned int nameandsig_hash(NameAndSig const& field) {
46+
Symbol* name = field._name;
47+
return (unsigned int) name->identity_hash();
48+
}
49+
50+
static inline bool nameandsig_equals(NameAndSig const& f1, NameAndSig const& f2) {
51+
return f1._name == f2._name &&
52+
f1._signature == f2._signature;
53+
}
54+
55+
// Maps a strict field's name and signature to whether or not it was initialized
56+
typedef ResourceHashtable<NameAndSig, bool, 17,
57+
AnyObj::RESOURCE_AREA, mtInternal,
58+
nameandsig_hash, nameandsig_equals> AssertUnsetFieldTable;
4459
private:
4560
int32_t _offset;
4661

@@ -60,6 +75,8 @@ class StackMapFrame : public ResourceObj {
6075
VerificationType* _locals; // local variable type array
6176
VerificationType* _stack; // operand stack type array
6277

78+
AssertUnsetFieldTable* _assert_unset_fields; // List of unsatisfied strict fields in the basic block
79+
6380
ClassVerifier* _verifier; // the verifier verifying this method
6481

6582
StackMapFrame(const StackMapFrame& cp) :
@@ -85,6 +102,7 @@ class StackMapFrame : public ResourceObj {
85102
_stack[i] = VerificationType::bogus_type();
86103
}
87104
}
105+
_assert_unset_fields = cp._assert_unset_fields;
88106
_verifier = nullptr;
89107
}
90108

@@ -94,7 +112,7 @@ class StackMapFrame : public ResourceObj {
94112
// This constructor is used by the type checker to allocate frames
95113
// in type state, which have _max_locals and _max_stack array elements
96114
// in _locals and _stack.
97-
StackMapFrame(u2 max_locals, u2 max_stack, ClassVerifier* verifier);
115+
StackMapFrame(u2 max_locals, u2 max_stack, AssertUnsetFieldTable* initial_strict_fields, ClassVerifier* verifier);
98116

99117
// This constructor is used to initialize stackmap frames in stackmap table,
100118
// which have _locals_size and _stack_size array elements in _locals and _stack.
@@ -106,13 +124,15 @@ class StackMapFrame : public ResourceObj {
106124
u2 max_stack,
107125
VerificationType* locals,
108126
VerificationType* stack,
127+
AssertUnsetFieldTable* assert_unset_fields,
109128
ClassVerifier* v) : _offset(offset),
110129
_locals_size(locals_size),
111130
_stack_size(stack_size),
112131
_stack_mark(-1),
113132
_max_locals(max_locals),
114133
_max_stack(max_stack), _flags(flags),
115134
_locals(locals), _stack(stack),
135+
_assert_unset_fields(assert_unset_fields),
116136
_verifier(v) { }
117137

118138
static StackMapFrame* copy(StackMapFrame* smf) {
@@ -136,6 +156,66 @@ class StackMapFrame : public ResourceObj {
136156
inline u2 max_stack() const { return _max_stack; }
137157
inline bool flag_this_uninit() const { return _flags & FLAG_THIS_UNINIT; }
138158

159+
AssertUnsetFieldTable* assert_unset_fields() const {
160+
return _assert_unset_fields;
161+
}
162+
163+
void set_assert_unset_fields(AssertUnsetFieldTable* table) {
164+
_assert_unset_fields = table;
165+
}
166+
167+
// Called when verifying putfields to mark strict instance fields as satisfied
168+
bool satisfy_unset_field(Symbol* name, Symbol* signature) {
169+
NameAndSig dummy_field(name, signature);
170+
171+
if (_assert_unset_fields->contains(dummy_field)) {
172+
_assert_unset_fields->put(dummy_field, true);
173+
return true;
174+
}
175+
return false;
176+
}
177+
178+
// Verify that all strict fields have been initialized
179+
// Strict fields must be initialized before the super constructor is called
180+
bool verify_unset_fields_satisfied() {
181+
bool all_satisfied = true;
182+
auto check_satisfied = [&] (const NameAndSig& key, const bool& value) {
183+
all_satisfied &= value;
184+
};
185+
_assert_unset_fields->iterate_all(check_satisfied);
186+
return all_satisfied;
187+
}
188+
189+
// Merge incoming unset strict fields from StackMapTable with
190+
// initial strict instance fields
191+
AssertUnsetFieldTable* merge_unset_fields(AssertUnsetFieldTable* new_fields) {
192+
auto merge_satisfied = [&] (const NameAndSig& key, const bool& value) {
193+
if (!new_fields->contains(key)) {
194+
new_fields->put(key, true);
195+
}
196+
};
197+
_assert_unset_fields->iterate_all(merge_satisfied);
198+
return new_fields;
199+
}
200+
201+
// Verify that strict fields are compatible between the current frame and the successor
202+
// Called during merging of frames
203+
bool verify_unset_fields_compatibility(AssertUnsetFieldTable* target_table) const {
204+
bool compatible = true;
205+
auto is_unset = [&] (const NameAndSig& key, const bool& value) {
206+
// Successor must have same debts as current frame
207+
if (!value) {
208+
if (*target_table->get(key) == true) {
209+
compatible = false;
210+
}
211+
}
212+
};
213+
_assert_unset_fields->iterate_all(is_unset);
214+
return compatible;
215+
}
216+
217+
static void print_strict_fields(AssertUnsetFieldTable* table);
218+
139219
// Set locals and stack types to bogus
140220
inline void reset() {
141221
int32_t i;

src/hotspot/share/classfile/stackMapTable.cpp

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ bool StackMapTable::match_stackmap(
127127
frame->set_stack_size(ssize);
128128
frame->copy_stack(stackmap_frame);
129129
frame->set_flags(stackmap_frame->flags());
130+
frame->set_assert_unset_fields(stackmap_frame->assert_unset_fields());
130131
}
131132
return result;
132133
}
@@ -157,11 +158,13 @@ void StackMapTable::print_on(outputStream* str) const {
157158
StackMapReader::StackMapReader(ClassVerifier* v, StackMapStream* stream,
158159
char* code_data, int32_t code_len,
159160
StackMapFrame* init_frame,
160-
u2 max_locals, u2 max_stack, TRAPS) :
161+
u2 max_locals, u2 max_stack,
162+
StackMapFrame::AssertUnsetFieldTable* initial_strict_fields, TRAPS) :
161163
_verifier(v), _stream(stream), _code_data(code_data),
162164
_code_length(code_len), _parsed_frame_count(0),
163165
_prev_frame(init_frame), _max_locals(max_locals),
164-
_max_stack(max_stack), _first(true) {
166+
_max_stack(max_stack), _assert_unset_fields_buffer(initial_strict_fields),
167+
_first(true) {
165168
methodHandle m = v->method();
166169
if (m->has_stackmap_table()) {
167170
_cp = constantPoolHandle(THREAD, m->constants());
@@ -245,6 +248,39 @@ StackMapFrame* StackMapReader::next_helper(TRAPS) {
245248
int offset;
246249
VerificationType* locals = nullptr;
247250
u1 frame_type = _stream->get_u1(CHECK_NULL);
251+
if (frame_type == ASSERT_UNSET_FIELDS) {
252+
u2 num_unset_fields = _stream->get_u2(CHECK_NULL);
253+
StackMapFrame::AssertUnsetFieldTable* new_fields = new StackMapFrame::AssertUnsetFieldTable();
254+
255+
for (u2 i = 0; i < num_unset_fields; i++) {
256+
u2 index = _stream->get_u2(CHECK_NULL);
257+
Symbol* name = _cp->symbol_at(_cp->name_ref_index_at(index));
258+
Symbol* sig = _cp->symbol_at(_cp->signature_ref_index_at(index));
259+
NameAndSig tmp(name, sig);
260+
261+
if (!_prev_frame->assert_unset_fields()->contains(tmp)) {
262+
ResourceMark rm(THREAD);
263+
log_info(verification)("Field %s%s is not found among initial strict instance fields", name->as_C_string(), sig->as_C_string());
264+
StackMapFrame::print_strict_fields(_prev_frame->assert_unset_fields());
265+
_prev_frame->verifier()->verify_error(
266+
ErrorContext::bad_strict_fields(_prev_frame->offset(), _prev_frame),
267+
"Strict fields not a subset of initial strict instance fields");
268+
} else {
269+
new_fields->put(tmp, false);
270+
}
271+
}
272+
273+
// Only modify strict instance fields the frame has uninitialized this
274+
if (_prev_frame->flag_this_uninit()) {
275+
_assert_unset_fields_buffer = _prev_frame->merge_unset_fields(new_fields);
276+
} else if (new_fields->number_of_entries() > 0) {
277+
_prev_frame->verifier()->verify_error(
278+
ErrorContext::bad_strict_fields(_prev_frame->offset(), _prev_frame),
279+
"Cannot have uninitialized strict fields after class initialization");
280+
}
281+
282+
return nullptr;
283+
}
248284
if (frame_type < 64) {
249285
// same_frame
250286
if (_first) {
@@ -260,7 +296,8 @@ StackMapFrame* StackMapReader::next_helper(TRAPS) {
260296
}
261297
frame = new StackMapFrame(
262298
offset, _prev_frame->flags(), _prev_frame->locals_size(), 0,
263-
_max_locals, _max_stack, locals, nullptr, _verifier);
299+
_max_locals, _max_stack, locals, nullptr,
300+
_assert_unset_fields_buffer, _verifier);
264301
if (_first && locals != nullptr) {
265302
frame->copy_locals(_prev_frame);
266303
}
@@ -292,7 +329,8 @@ StackMapFrame* StackMapReader::next_helper(TRAPS) {
292329
stack_size, _max_stack, CHECK_VERIFY_(_verifier, nullptr));
293330
frame = new StackMapFrame(
294331
offset, _prev_frame->flags(), _prev_frame->locals_size(), stack_size,
295-
_max_locals, _max_stack, locals, stack, _verifier);
332+
_max_locals, _max_stack, locals, stack,
333+
_assert_unset_fields_buffer, _verifier);
296334
if (_first && locals != nullptr) {
297335
frame->copy_locals(_prev_frame);
298336
}
@@ -302,7 +340,7 @@ StackMapFrame* StackMapReader::next_helper(TRAPS) {
302340

303341
u2 offset_delta = _stream->get_u2(CHECK_NULL);
304342

305-
if (frame_type < SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
343+
if (frame_type < ASSERT_UNSET_FIELDS) {
306344
// reserved frame types
307345
_stream->stackmap_format_error(
308346
"reserved frame type", CHECK_VERIFY_(_verifier, nullptr));
@@ -333,7 +371,8 @@ StackMapFrame* StackMapReader::next_helper(TRAPS) {
333371
stack_size, _max_stack, CHECK_VERIFY_(_verifier, nullptr));
334372
frame = new StackMapFrame(
335373
offset, _prev_frame->flags(), _prev_frame->locals_size(), stack_size,
336-
_max_locals, _max_stack, locals, stack, _verifier);
374+
_max_locals, _max_stack, locals, stack,
375+
_assert_unset_fields_buffer, _verifier);
337376
if (_first && locals != nullptr) {
338377
frame->copy_locals(_prev_frame);
339378
}
@@ -375,7 +414,8 @@ StackMapFrame* StackMapReader::next_helper(TRAPS) {
375414
}
376415
frame = new StackMapFrame(
377416
offset, flags, new_length, 0, _max_locals, _max_stack,
378-
locals, nullptr, _verifier);
417+
locals, nullptr,
418+
_assert_unset_fields_buffer, _verifier);
379419
if (_first && locals != nullptr) {
380420
frame->copy_locals(_prev_frame);
381421
}
@@ -409,7 +449,8 @@ StackMapFrame* StackMapReader::next_helper(TRAPS) {
409449
}
410450
frame = new StackMapFrame(
411451
offset, flags, real_length, 0, _max_locals,
412-
_max_stack, locals, nullptr, _verifier);
452+
_max_stack, locals, nullptr,
453+
_assert_unset_fields_buffer, _verifier);
413454
_first = false;
414455
return frame;
415456
}
@@ -457,7 +498,8 @@ StackMapFrame* StackMapReader::next_helper(TRAPS) {
457498
}
458499
frame = new StackMapFrame(
459500
offset, flags, real_locals_size, real_stack_size,
460-
_max_locals, _max_stack, locals, stack, _verifier);
501+
_max_locals, _max_stack, locals, stack,
502+
_assert_unset_fields_buffer, _verifier);
461503
_first = false;
462504
return frame;
463505
}

src/hotspot/share/classfile/stackMapTable.hpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -127,6 +127,9 @@ class StackMapReader : StackObj {
127127
u2 _max_locals;
128128
u2 _max_stack;
129129

130+
// Contains assert_unset_fields generated from classfile
131+
StackMapFrame::AssertUnsetFieldTable* _assert_unset_fields_buffer;
132+
130133
// Check if reading first entry
131134
bool _first;
132135

@@ -148,6 +151,7 @@ class StackMapReader : StackObj {
148151
}
149152

150153
enum {
154+
ASSERT_UNSET_FIELDS = 246,
151155
SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247,
152156
SAME_EXTENDED = 251,
153157
FULL = 255
@@ -158,7 +162,8 @@ class StackMapReader : StackObj {
158162
StackMapReader(ClassVerifier* v, StackMapStream* stream,
159163
char* code_data, int32_t code_len,
160164
StackMapFrame* init_frame,
161-
u2 max_locals, u2 max_stack, TRAPS);
165+
u2 max_locals, u2 max_stack,
166+
StackMapFrame::AssertUnsetFieldTable* initial_strict_fields, TRAPS);
162167

163168
inline int32_t get_frame_count() const { return _frame_count; }
164169
inline StackMapFrame* prev_frame() const { return _prev_frame; }

0 commit comments

Comments
 (0)