ruby/internal/struct.h
Jean Boussier 73be9992e9 Disambiguate private and public RSTRUCT_ helpers
RSTRUCT_LEN / RSTRUCT_GET / RSTRUCT_SET all existing in two
versions, one public that does type and frozens checks
and one private that doesn't.

The problem is that this is error prone because the public version
is always accessible, but the private one require to include
`internal/struct.h`. So you may have some code that rely on the
public version, and later on the private header is included and
changes the behavior.

This already led to introducing a bug in YJIT & ZJIT:
https://github.com/ruby/ruby/pull/15835
2026-01-11 13:03:03 +01:00

141 lines
4.0 KiB
C

#ifndef INTERNAL_STRUCT_H /*-*-C-*-vi:se ft=c:*/
#define INTERNAL_STRUCT_H
/**
* @author Ruby developers <ruby-core@ruby-lang.org>
* @copyright This file is a part of the programming language Ruby.
* Permission is hereby granted, to either redistribute and/or
* modify this file, provided that the conditions mentioned in the
* file COPYING are met. Consult the file for details.
* @brief Internal header for Struct.
*/
#include "ruby/internal/stdbool.h" /* for bool */
#include "ruby/ruby.h" /* for struct RBasic */
/* Flags of RStruct
*
* 1-7: RSTRUCT_EMBED_LEN
* If non-zero, the struct is embedded (its contents follow the
* header, rather than being on a separately allocated buffer) and
* these bits are the length of the Struct.
* 8: RSTRUCT_GEN_FIELDS
* The struct is embedded and has no space left to store the
* IMEMO/fields reference. Any ivar this struct may have will be in
* the generic_fields_tbl. This flag doesn't imply the struct has
* ivars.
*/
enum {
RSTRUCT_EMBED_LEN_MASK = RUBY_FL_USER7 | RUBY_FL_USER6 | RUBY_FL_USER5 | RUBY_FL_USER4 |
RUBY_FL_USER3 | RUBY_FL_USER2 | RUBY_FL_USER1,
RSTRUCT_EMBED_LEN_SHIFT = (RUBY_FL_USHIFT+1),
RSTRUCT_GEN_FIELDS = RUBY_FL_USER8,
};
struct RStruct {
struct RBasic basic;
union {
struct {
long len;
const VALUE *ptr;
VALUE fields_obj;
} heap;
/* This is a length 1 array because:
* 1. GCC has a bug that does not optimize C flexible array members
* (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102452)
* 2. Zero length arrays are not supported by all compilers
*/
const VALUE ary[1];
} as;
};
#define RSTRUCT(obj) ((struct RStruct *)(obj))
/* struct.c */
VALUE rb_struct_init_copy(VALUE copy, VALUE s);
VALUE rb_struct_lookup(VALUE s, VALUE idx);
VALUE rb_struct_s_keyword_init(VALUE klass);
static inline long RSTRUCT_EMBED_LEN(VALUE st);
static inline long RSTRUCT_LEN_RAW(VALUE st);
static inline int RSTRUCT_LENINT(VALUE st);
static inline const VALUE *RSTRUCT_CONST_PTR(VALUE st);
static inline void RSTRUCT_SET_RAW(VALUE st, long k, VALUE v);
static inline VALUE RSTRUCT_GET_RAW(VALUE st, long k);
static inline long
RSTRUCT_EMBED_LEN(VALUE st)
{
long ret = FL_TEST_RAW(st, RSTRUCT_EMBED_LEN_MASK);
ret >>= RSTRUCT_EMBED_LEN_SHIFT;
return ret;
}
static inline long
RSTRUCT_LEN_RAW(VALUE st)
{
if (FL_TEST_RAW(st, RSTRUCT_EMBED_LEN_MASK)) {
return RSTRUCT_EMBED_LEN(st);
}
else {
return RSTRUCT(st)->as.heap.len;
}
}
static inline int
RSTRUCT_LENINT(VALUE st)
{
return rb_long2int(RSTRUCT_LEN_RAW(st));
}
static inline const VALUE *
RSTRUCT_CONST_PTR(VALUE st)
{
const struct RStruct *p = RSTRUCT(st);
if (FL_TEST_RAW(st, RSTRUCT_EMBED_LEN_MASK)) {
return p->as.ary;
}
else {
return p->as.heap.ptr;
}
}
static inline void
RSTRUCT_SET_RAW(VALUE st, long k, VALUE v)
{
RB_OBJ_WRITE(st, &RSTRUCT_CONST_PTR(st)[k], v);
}
static inline VALUE
RSTRUCT_GET_RAW(VALUE st, long k)
{
return RSTRUCT_CONST_PTR(st)[k];
}
static inline VALUE
RSTRUCT_FIELDS_OBJ(VALUE st)
{
const long embed_len = RSTRUCT_EMBED_LEN(st);
VALUE fields_obj;
if (embed_len) {
RUBY_ASSERT(!FL_TEST_RAW(st, RSTRUCT_GEN_FIELDS));
fields_obj = RSTRUCT_GET_RAW(st, embed_len);
}
else {
fields_obj = RSTRUCT(st)->as.heap.fields_obj;
}
return fields_obj;
}
static inline void
RSTRUCT_SET_FIELDS_OBJ(VALUE st, VALUE fields_obj)
{
const long embed_len = RSTRUCT_EMBED_LEN(st);
if (embed_len) {
RUBY_ASSERT(!FL_TEST_RAW(st, RSTRUCT_GEN_FIELDS));
RSTRUCT_SET_RAW(st, embed_len, fields_obj);
}
else {
RB_OBJ_WRITE(st, &RSTRUCT(st)->as.heap.fields_obj, fields_obj);
}
}
#endif /* INTERNAL_STRUCT_H */