@@ -252,10 +252,21 @@ static const rb_data_type_t BigDecimal_data_type = {
252252#endif
253253};
254254
255+ // TypedData_Wrap_Struct may fail if there is no memory, or GC.add_stress_to_class(BigDecimal) is set.
256+ // We need to first allocate empty struct, allocate Real struct, and then set the data pointer.
257+ typedef struct { VALUE _obj ; } NULL_WRAPPED_VALUE ;
258+ static NULL_WRAPPED_VALUE
259+ BigDecimal_alloc_empty_struct (VALUE klass )
260+ {
261+ return (NULL_WRAPPED_VALUE ) { TypedData_Wrap_Struct (klass , & BigDecimal_data_type , NULL ) };
262+ }
263+
255264static VALUE
256- BigDecimal_wrap_struct (VALUE klass , Real * real )
265+ BigDecimal_wrap_struct (NULL_WRAPPED_VALUE v , Real * real )
257266{
258- VALUE obj = TypedData_Wrap_Struct (klass , & BigDecimal_data_type , real );
267+ VALUE obj = v ._obj ;
268+ assert (RTYPEDDATA_DATA (obj ) == NULL );
269+ RTYPEDDATA_DATA (obj ) = real ;
259270 RB_OBJ_FREEZE (obj );
260271 return obj ;
261272}
@@ -265,8 +276,9 @@ MAYBE_UNUSED(static inline BDVALUE rbd_allocate_struct_zero_wrap(int sign, size_
265276static BDVALUE
266277rbd_allocate_struct_zero_wrap (int sign , size_t const digits )
267278{
279+ NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct (rb_cBigDecimal );
268280 Real * real = rbd_allocate_struct_zero (sign , digits );
269- return (BDVALUE ) { BigDecimal_wrap_struct (rb_cBigDecimal , real ), real };
281+ return (BDVALUE ) { BigDecimal_wrap_struct (null_wrapped , real ), real };
270282}
271283
272284static inline int
@@ -1041,9 +1053,10 @@ check_int_precision(VALUE v)
10411053static NULLABLE_BDVALUE
10421054CreateFromString (const char * str , VALUE klass , bool strict_p , bool raise_exception )
10431055{
1056+ NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct (klass );
10441057 Real * pv = VpAlloc (str , strict_p , raise_exception );
10451058 if (!pv ) return (NULLABLE_BDVALUE ) { Qnil , NULL };
1046- return (NULLABLE_BDVALUE ) { BigDecimal_wrap_struct (klass , pv ), pv };
1059+ return (NULLABLE_BDVALUE ) { BigDecimal_wrap_struct (null_wrapped , pv ), pv };
10471060}
10481061
10491062static Real *
@@ -2580,6 +2593,7 @@ check_exception(VALUE bd)
25802593static VALUE
25812594rb_uint64_convert_to_BigDecimal (uint64_t uval )
25822595{
2596+ NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct (rb_cBigDecimal );
25832597 Real * vp ;
25842598 if (uval == 0 ) {
25852599 vp = rbd_allocate_struct (1 );
@@ -2621,7 +2635,7 @@ rb_uint64_convert_to_BigDecimal(uint64_t uval)
26212635 MEMCPY (vp -> frac , buf + BIGDECIMAL_INT64_MAX_LENGTH - len , DECDIG , len );
26222636 }
26232637
2624- return BigDecimal_wrap_struct (rb_cBigDecimal , vp );
2638+ return BigDecimal_wrap_struct (null_wrapped , vp );
26252639}
26262640
26272641static VALUE
@@ -2905,12 +2919,13 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
29052919 if (digs == SIZE_MAX )
29062920 return check_exception (val );
29072921
2922+ NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct (rb_cBigDecimal );
29082923 Real * vp ;
29092924 TypedData_Get_Struct (val , Real , & BigDecimal_data_type , vp );
29102925 vp = VpCopy (NULL , vp );
29112926 RB_GC_GUARD (val );
29122927
2913- VALUE copy = BigDecimal_wrap_struct (rb_cBigDecimal , vp );
2928+ VALUE copy = BigDecimal_wrap_struct (null_wrapped , vp );
29142929 /* TODO: rounding */
29152930 return check_exception (copy );
29162931 }
0 commit comments