diff options
Diffstat (limited to 'ext')
430 files changed, 22164 insertions, 43508 deletions
diff --git a/ext/-test-/RUBY_ALIGNOF/depend b/ext/-test-/RUBY_ALIGNOF/depend index 3011b637e5..103d20b33c 100644 --- a/ext/-test-/RUBY_ALIGNOF/depend +++ b/ext/-test-/RUBY_ALIGNOF/depend @@ -128,6 +128,7 @@ c.o: $(hdrdir)/ruby/internal/intern/re.h c.o: $(hdrdir)/ruby/internal/intern/ruby.h c.o: $(hdrdir)/ruby/internal/intern/select.h c.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +c.o: $(hdrdir)/ruby/internal/intern/set.h c.o: $(hdrdir)/ruby/internal/intern/signal.h c.o: $(hdrdir)/ruby/internal/intern/sprintf.h c.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ c.o: $(hdrdir)/ruby/internal/special_consts.h c.o: $(hdrdir)/ruby/internal/static_assert.h c.o: $(hdrdir)/ruby/internal/stdalign.h c.o: $(hdrdir)/ruby/internal/stdbool.h +c.o: $(hdrdir)/ruby/internal/stdckdint.h c.o: $(hdrdir)/ruby/internal/symbol.h c.o: $(hdrdir)/ruby/internal/value.h c.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/abi/depend b/ext/-test-/abi/depend new file mode 100644 index 0000000000..716a7b1356 --- /dev/null +++ b/ext/-test-/abi/depend @@ -0,0 +1,3 @@ +# AUTOGENERATED DEPENDENCIES START +abi.o: abi.c +# AUTOGENERATED DEPENDENCIES END diff --git a/ext/-test-/arith_seq/beg_len_step/depend b/ext/-test-/arith_seq/beg_len_step/depend index dc807eaa99..098c8ff1f0 100644 --- a/ext/-test-/arith_seq/beg_len_step/depend +++ b/ext/-test-/arith_seq/beg_len_step/depend @@ -127,6 +127,7 @@ beg_len_step.o: $(hdrdir)/ruby/internal/intern/re.h beg_len_step.o: $(hdrdir)/ruby/internal/intern/ruby.h beg_len_step.o: $(hdrdir)/ruby/internal/intern/select.h beg_len_step.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +beg_len_step.o: $(hdrdir)/ruby/internal/intern/set.h beg_len_step.o: $(hdrdir)/ruby/internal/intern/signal.h beg_len_step.o: $(hdrdir)/ruby/internal/intern/sprintf.h beg_len_step.o: $(hdrdir)/ruby/internal/intern/string.h @@ -146,6 +147,7 @@ beg_len_step.o: $(hdrdir)/ruby/internal/special_consts.h beg_len_step.o: $(hdrdir)/ruby/internal/static_assert.h beg_len_step.o: $(hdrdir)/ruby/internal/stdalign.h beg_len_step.o: $(hdrdir)/ruby/internal/stdbool.h +beg_len_step.o: $(hdrdir)/ruby/internal/stdckdint.h beg_len_step.o: $(hdrdir)/ruby/internal/symbol.h beg_len_step.o: $(hdrdir)/ruby/internal/value.h beg_len_step.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/arith_seq/extract/depend b/ext/-test-/arith_seq/extract/depend index 231736b277..5c07cea4b4 100644 --- a/ext/-test-/arith_seq/extract/depend +++ b/ext/-test-/arith_seq/extract/depend @@ -127,6 +127,7 @@ extract.o: $(hdrdir)/ruby/internal/intern/re.h extract.o: $(hdrdir)/ruby/internal/intern/ruby.h extract.o: $(hdrdir)/ruby/internal/intern/select.h extract.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +extract.o: $(hdrdir)/ruby/internal/intern/set.h extract.o: $(hdrdir)/ruby/internal/intern/signal.h extract.o: $(hdrdir)/ruby/internal/intern/sprintf.h extract.o: $(hdrdir)/ruby/internal/intern/string.h @@ -146,6 +147,7 @@ extract.o: $(hdrdir)/ruby/internal/special_consts.h extract.o: $(hdrdir)/ruby/internal/static_assert.h extract.o: $(hdrdir)/ruby/internal/stdalign.h extract.o: $(hdrdir)/ruby/internal/stdbool.h +extract.o: $(hdrdir)/ruby/internal/stdckdint.h extract.o: $(hdrdir)/ruby/internal/symbol.h extract.o: $(hdrdir)/ruby/internal/value.h extract.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/array/concat/depend b/ext/-test-/array/concat/depend index d66e7a540f..8edf45465f 100644 --- a/ext/-test-/array/concat/depend +++ b/ext/-test-/array/concat/depend @@ -128,6 +128,7 @@ to_ary_concat.o: $(hdrdir)/ruby/internal/intern/re.h to_ary_concat.o: $(hdrdir)/ruby/internal/intern/ruby.h to_ary_concat.o: $(hdrdir)/ruby/internal/intern/select.h to_ary_concat.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +to_ary_concat.o: $(hdrdir)/ruby/internal/intern/set.h to_ary_concat.o: $(hdrdir)/ruby/internal/intern/signal.h to_ary_concat.o: $(hdrdir)/ruby/internal/intern/sprintf.h to_ary_concat.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ to_ary_concat.o: $(hdrdir)/ruby/internal/special_consts.h to_ary_concat.o: $(hdrdir)/ruby/internal/static_assert.h to_ary_concat.o: $(hdrdir)/ruby/internal/stdalign.h to_ary_concat.o: $(hdrdir)/ruby/internal/stdbool.h +to_ary_concat.o: $(hdrdir)/ruby/internal/stdckdint.h to_ary_concat.o: $(hdrdir)/ruby/internal/symbol.h to_ary_concat.o: $(hdrdir)/ruby/internal/value.h to_ary_concat.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/array/resize/depend b/ext/-test-/array/resize/depend index a9c02b3db2..e6a228b43d 100644 --- a/ext/-test-/array/resize/depend +++ b/ext/-test-/array/resize/depend @@ -127,6 +127,7 @@ resize.o: $(hdrdir)/ruby/internal/intern/re.h resize.o: $(hdrdir)/ruby/internal/intern/ruby.h resize.o: $(hdrdir)/ruby/internal/intern/select.h resize.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +resize.o: $(hdrdir)/ruby/internal/intern/set.h resize.o: $(hdrdir)/ruby/internal/intern/signal.h resize.o: $(hdrdir)/ruby/internal/intern/sprintf.h resize.o: $(hdrdir)/ruby/internal/intern/string.h @@ -146,6 +147,7 @@ resize.o: $(hdrdir)/ruby/internal/special_consts.h resize.o: $(hdrdir)/ruby/internal/static_assert.h resize.o: $(hdrdir)/ruby/internal/stdalign.h resize.o: $(hdrdir)/ruby/internal/stdbool.h +resize.o: $(hdrdir)/ruby/internal/stdckdint.h resize.o: $(hdrdir)/ruby/internal/symbol.h resize.o: $(hdrdir)/ruby/internal/value.h resize.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/bignum/depend b/ext/-test-/bignum/depend index d4392bb6a1..82972f1032 100644 --- a/ext/-test-/bignum/depend +++ b/ext/-test-/bignum/depend @@ -6,6 +6,7 @@ big2str.o: $(hdrdir)/ruby/backward.h big2str.o: $(hdrdir)/ruby/backward/2/assume.h big2str.o: $(hdrdir)/ruby/backward/2/attributes.h big2str.o: $(hdrdir)/ruby/backward/2/bool.h +big2str.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h big2str.o: $(hdrdir)/ruby/backward/2/inttypes.h big2str.o: $(hdrdir)/ruby/backward/2/limits.h big2str.o: $(hdrdir)/ruby/backward/2/long_long.h @@ -127,6 +128,7 @@ big2str.o: $(hdrdir)/ruby/internal/intern/re.h big2str.o: $(hdrdir)/ruby/internal/intern/ruby.h big2str.o: $(hdrdir)/ruby/internal/intern/select.h big2str.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +big2str.o: $(hdrdir)/ruby/internal/intern/set.h big2str.o: $(hdrdir)/ruby/internal/intern/signal.h big2str.o: $(hdrdir)/ruby/internal/intern/sprintf.h big2str.o: $(hdrdir)/ruby/internal/intern/string.h @@ -146,6 +148,7 @@ big2str.o: $(hdrdir)/ruby/internal/special_consts.h big2str.o: $(hdrdir)/ruby/internal/static_assert.h big2str.o: $(hdrdir)/ruby/internal/stdalign.h big2str.o: $(hdrdir)/ruby/internal/stdbool.h +big2str.o: $(hdrdir)/ruby/internal/stdckdint.h big2str.o: $(hdrdir)/ruby/internal/symbol.h big2str.o: $(hdrdir)/ruby/internal/value.h big2str.o: $(hdrdir)/ruby/internal/value_type.h @@ -157,6 +160,7 @@ big2str.o: $(hdrdir)/ruby/ruby.h big2str.o: $(hdrdir)/ruby/st.h big2str.o: $(hdrdir)/ruby/subst.h big2str.o: $(top_srcdir)/internal/bignum.h +big2str.o: $(top_srcdir)/internal/compilers.h big2str.o: big2str.c bigzero.o: $(RUBY_EXTCONF_H) bigzero.o: $(arch_hdrdir)/ruby/config.h @@ -165,6 +169,7 @@ bigzero.o: $(hdrdir)/ruby/backward.h bigzero.o: $(hdrdir)/ruby/backward/2/assume.h bigzero.o: $(hdrdir)/ruby/backward/2/attributes.h bigzero.o: $(hdrdir)/ruby/backward/2/bool.h +bigzero.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h bigzero.o: $(hdrdir)/ruby/backward/2/inttypes.h bigzero.o: $(hdrdir)/ruby/backward/2/limits.h bigzero.o: $(hdrdir)/ruby/backward/2/long_long.h @@ -286,6 +291,7 @@ bigzero.o: $(hdrdir)/ruby/internal/intern/re.h bigzero.o: $(hdrdir)/ruby/internal/intern/ruby.h bigzero.o: $(hdrdir)/ruby/internal/intern/select.h bigzero.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +bigzero.o: $(hdrdir)/ruby/internal/intern/set.h bigzero.o: $(hdrdir)/ruby/internal/intern/signal.h bigzero.o: $(hdrdir)/ruby/internal/intern/sprintf.h bigzero.o: $(hdrdir)/ruby/internal/intern/string.h @@ -305,6 +311,7 @@ bigzero.o: $(hdrdir)/ruby/internal/special_consts.h bigzero.o: $(hdrdir)/ruby/internal/static_assert.h bigzero.o: $(hdrdir)/ruby/internal/stdalign.h bigzero.o: $(hdrdir)/ruby/internal/stdbool.h +bigzero.o: $(hdrdir)/ruby/internal/stdckdint.h bigzero.o: $(hdrdir)/ruby/internal/symbol.h bigzero.o: $(hdrdir)/ruby/internal/value.h bigzero.o: $(hdrdir)/ruby/internal/value_type.h @@ -316,6 +323,7 @@ bigzero.o: $(hdrdir)/ruby/ruby.h bigzero.o: $(hdrdir)/ruby/st.h bigzero.o: $(hdrdir)/ruby/subst.h bigzero.o: $(top_srcdir)/internal/bignum.h +bigzero.o: $(top_srcdir)/internal/compilers.h bigzero.o: bigzero.c div.o: $(RUBY_EXTCONF_H) div.o: $(arch_hdrdir)/ruby/config.h @@ -324,6 +332,7 @@ div.o: $(hdrdir)/ruby/backward.h div.o: $(hdrdir)/ruby/backward/2/assume.h div.o: $(hdrdir)/ruby/backward/2/attributes.h div.o: $(hdrdir)/ruby/backward/2/bool.h +div.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h div.o: $(hdrdir)/ruby/backward/2/inttypes.h div.o: $(hdrdir)/ruby/backward/2/limits.h div.o: $(hdrdir)/ruby/backward/2/long_long.h @@ -445,6 +454,7 @@ div.o: $(hdrdir)/ruby/internal/intern/re.h div.o: $(hdrdir)/ruby/internal/intern/ruby.h div.o: $(hdrdir)/ruby/internal/intern/select.h div.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +div.o: $(hdrdir)/ruby/internal/intern/set.h div.o: $(hdrdir)/ruby/internal/intern/signal.h div.o: $(hdrdir)/ruby/internal/intern/sprintf.h div.o: $(hdrdir)/ruby/internal/intern/string.h @@ -464,6 +474,7 @@ div.o: $(hdrdir)/ruby/internal/special_consts.h div.o: $(hdrdir)/ruby/internal/static_assert.h div.o: $(hdrdir)/ruby/internal/stdalign.h div.o: $(hdrdir)/ruby/internal/stdbool.h +div.o: $(hdrdir)/ruby/internal/stdckdint.h div.o: $(hdrdir)/ruby/internal/symbol.h div.o: $(hdrdir)/ruby/internal/value.h div.o: $(hdrdir)/ruby/internal/value_type.h @@ -475,6 +486,7 @@ div.o: $(hdrdir)/ruby/ruby.h div.o: $(hdrdir)/ruby/st.h div.o: $(hdrdir)/ruby/subst.h div.o: $(top_srcdir)/internal/bignum.h +div.o: $(top_srcdir)/internal/compilers.h div.o: div.c init.o: $(RUBY_EXTCONF_H) init.o: $(arch_hdrdir)/ruby/config.h @@ -605,6 +617,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -624,6 +637,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -642,6 +656,7 @@ intpack.o: $(hdrdir)/ruby/backward.h intpack.o: $(hdrdir)/ruby/backward/2/assume.h intpack.o: $(hdrdir)/ruby/backward/2/attributes.h intpack.o: $(hdrdir)/ruby/backward/2/bool.h +intpack.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h intpack.o: $(hdrdir)/ruby/backward/2/inttypes.h intpack.o: $(hdrdir)/ruby/backward/2/limits.h intpack.o: $(hdrdir)/ruby/backward/2/long_long.h @@ -763,6 +778,7 @@ intpack.o: $(hdrdir)/ruby/internal/intern/re.h intpack.o: $(hdrdir)/ruby/internal/intern/ruby.h intpack.o: $(hdrdir)/ruby/internal/intern/select.h intpack.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +intpack.o: $(hdrdir)/ruby/internal/intern/set.h intpack.o: $(hdrdir)/ruby/internal/intern/signal.h intpack.o: $(hdrdir)/ruby/internal/intern/sprintf.h intpack.o: $(hdrdir)/ruby/internal/intern/string.h @@ -782,6 +798,7 @@ intpack.o: $(hdrdir)/ruby/internal/special_consts.h intpack.o: $(hdrdir)/ruby/internal/static_assert.h intpack.o: $(hdrdir)/ruby/internal/stdalign.h intpack.o: $(hdrdir)/ruby/internal/stdbool.h +intpack.o: $(hdrdir)/ruby/internal/stdckdint.h intpack.o: $(hdrdir)/ruby/internal/symbol.h intpack.o: $(hdrdir)/ruby/internal/value.h intpack.o: $(hdrdir)/ruby/internal/value_type.h @@ -793,6 +810,7 @@ intpack.o: $(hdrdir)/ruby/ruby.h intpack.o: $(hdrdir)/ruby/st.h intpack.o: $(hdrdir)/ruby/subst.h intpack.o: $(top_srcdir)/internal/bignum.h +intpack.o: $(top_srcdir)/internal/compilers.h intpack.o: intpack.c mul.o: $(RUBY_EXTCONF_H) mul.o: $(arch_hdrdir)/ruby/config.h @@ -801,6 +819,7 @@ mul.o: $(hdrdir)/ruby/backward.h mul.o: $(hdrdir)/ruby/backward/2/assume.h mul.o: $(hdrdir)/ruby/backward/2/attributes.h mul.o: $(hdrdir)/ruby/backward/2/bool.h +mul.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h mul.o: $(hdrdir)/ruby/backward/2/inttypes.h mul.o: $(hdrdir)/ruby/backward/2/limits.h mul.o: $(hdrdir)/ruby/backward/2/long_long.h @@ -922,6 +941,7 @@ mul.o: $(hdrdir)/ruby/internal/intern/re.h mul.o: $(hdrdir)/ruby/internal/intern/ruby.h mul.o: $(hdrdir)/ruby/internal/intern/select.h mul.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +mul.o: $(hdrdir)/ruby/internal/intern/set.h mul.o: $(hdrdir)/ruby/internal/intern/signal.h mul.o: $(hdrdir)/ruby/internal/intern/sprintf.h mul.o: $(hdrdir)/ruby/internal/intern/string.h @@ -941,6 +961,7 @@ mul.o: $(hdrdir)/ruby/internal/special_consts.h mul.o: $(hdrdir)/ruby/internal/static_assert.h mul.o: $(hdrdir)/ruby/internal/stdalign.h mul.o: $(hdrdir)/ruby/internal/stdbool.h +mul.o: $(hdrdir)/ruby/internal/stdckdint.h mul.o: $(hdrdir)/ruby/internal/symbol.h mul.o: $(hdrdir)/ruby/internal/value.h mul.o: $(hdrdir)/ruby/internal/value_type.h @@ -952,6 +973,7 @@ mul.o: $(hdrdir)/ruby/ruby.h mul.o: $(hdrdir)/ruby/st.h mul.o: $(hdrdir)/ruby/subst.h mul.o: $(top_srcdir)/internal/bignum.h +mul.o: $(top_srcdir)/internal/compilers.h mul.o: mul.c str2big.o: $(RUBY_EXTCONF_H) str2big.o: $(arch_hdrdir)/ruby/config.h @@ -960,6 +982,7 @@ str2big.o: $(hdrdir)/ruby/backward.h str2big.o: $(hdrdir)/ruby/backward/2/assume.h str2big.o: $(hdrdir)/ruby/backward/2/attributes.h str2big.o: $(hdrdir)/ruby/backward/2/bool.h +str2big.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h str2big.o: $(hdrdir)/ruby/backward/2/inttypes.h str2big.o: $(hdrdir)/ruby/backward/2/limits.h str2big.o: $(hdrdir)/ruby/backward/2/long_long.h @@ -1081,6 +1104,7 @@ str2big.o: $(hdrdir)/ruby/internal/intern/re.h str2big.o: $(hdrdir)/ruby/internal/intern/ruby.h str2big.o: $(hdrdir)/ruby/internal/intern/select.h str2big.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +str2big.o: $(hdrdir)/ruby/internal/intern/set.h str2big.o: $(hdrdir)/ruby/internal/intern/signal.h str2big.o: $(hdrdir)/ruby/internal/intern/sprintf.h str2big.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1100,6 +1124,7 @@ str2big.o: $(hdrdir)/ruby/internal/special_consts.h str2big.o: $(hdrdir)/ruby/internal/static_assert.h str2big.o: $(hdrdir)/ruby/internal/stdalign.h str2big.o: $(hdrdir)/ruby/internal/stdbool.h +str2big.o: $(hdrdir)/ruby/internal/stdckdint.h str2big.o: $(hdrdir)/ruby/internal/symbol.h str2big.o: $(hdrdir)/ruby/internal/value.h str2big.o: $(hdrdir)/ruby/internal/value_type.h @@ -1111,5 +1136,6 @@ str2big.o: $(hdrdir)/ruby/ruby.h str2big.o: $(hdrdir)/ruby/st.h str2big.o: $(hdrdir)/ruby/subst.h str2big.o: $(top_srcdir)/internal/bignum.h +str2big.o: $(top_srcdir)/internal/compilers.h str2big.o: str2big.c # AUTOGENERATED DEPENDENCIES END diff --git a/ext/-test-/box/yay1/extconf.rb b/ext/-test-/box/yay1/extconf.rb new file mode 100644 index 0000000000..54387cedf1 --- /dev/null +++ b/ext/-test-/box/yay1/extconf.rb @@ -0,0 +1 @@ +create_makefile('-test-/box/yay1') diff --git a/ext/-test-/box/yay1/yay1.c b/ext/-test-/box/yay1/yay1.c new file mode 100644 index 0000000000..564a221c8c --- /dev/null +++ b/ext/-test-/box/yay1/yay1.c @@ -0,0 +1,28 @@ +#include "yay1.h" + +VALUE +yay_value(void) +{ + return rb_str_new_cstr("yay"); +} + +static VALUE +yay1_f_version(VALUE klass) +{ + return rb_str_new_cstr("1.0.0"); +} + +static VALUE +yay1_yay(VALUE klass) +{ + return yay_value(); +} + +void +Init_yay1(void) +{ + VALUE mod = rb_define_module("Yay"); + rb_define_const(mod, "VERSION", rb_str_new_cstr("1.0.0")); + rb_define_singleton_method(mod, "version", yay1_f_version, 0); + rb_define_singleton_method(mod, "yay", yay1_yay, 0); +} diff --git a/ext/-test-/box/yay1/yay1.def b/ext/-test-/box/yay1/yay1.def new file mode 100644 index 0000000000..510fbe7017 --- /dev/null +++ b/ext/-test-/box/yay1/yay1.def @@ -0,0 +1,3 @@ +EXPORTS + Init_yay1 + yay_value diff --git a/ext/-test-/box/yay1/yay1.h b/ext/-test-/box/yay1/yay1.h new file mode 100644 index 0000000000..c4dade928a --- /dev/null +++ b/ext/-test-/box/yay1/yay1.h @@ -0,0 +1,4 @@ +#include <ruby.h> +#include "ruby/internal/dllexport.h" + +RUBY_FUNC_EXPORTED VALUE yay_value(void); diff --git a/ext/-test-/box/yay2/extconf.rb b/ext/-test-/box/yay2/extconf.rb new file mode 100644 index 0000000000..850ef3edc9 --- /dev/null +++ b/ext/-test-/box/yay2/extconf.rb @@ -0,0 +1 @@ +create_makefile('-test-/box/yay2') diff --git a/ext/-test-/box/yay2/yay2.c b/ext/-test-/box/yay2/yay2.c new file mode 100644 index 0000000000..b632ae8495 --- /dev/null +++ b/ext/-test-/box/yay2/yay2.c @@ -0,0 +1,28 @@ +#include "yay2.h" + +VALUE +yay_value(void) +{ + return rb_str_new_cstr("yaaay"); +} + +static VALUE +yay2_f_version(VALUE klass) +{ + return rb_str_new_cstr("2.0.0"); +} + +static VALUE +yay2_yay(VALUE klass) +{ + return yay_value(); +} + +void +Init_yay2(void) +{ + VALUE mod = rb_define_module("Yay"); + rb_define_const(mod, "VERSION", rb_str_new_cstr("2.0.0")); + rb_define_singleton_method(mod, "version", yay2_f_version, 0); + rb_define_singleton_method(mod, "yay", yay2_yay, 0); +} diff --git a/ext/-test-/box/yay2/yay2.def b/ext/-test-/box/yay2/yay2.def new file mode 100644 index 0000000000..163fc44c04 --- /dev/null +++ b/ext/-test-/box/yay2/yay2.def @@ -0,0 +1,3 @@ +EXPORTS + Init_yay2 + yay_value diff --git a/ext/-test-/box/yay2/yay2.h b/ext/-test-/box/yay2/yay2.h new file mode 100644 index 0000000000..c4dade928a --- /dev/null +++ b/ext/-test-/box/yay2/yay2.h @@ -0,0 +1,4 @@ +#include <ruby.h> +#include "ruby/internal/dllexport.h" + +RUBY_FUNC_EXPORTED VALUE yay_value(void); diff --git a/ext/-test-/bug-14834/bug-14384.c b/ext/-test-/bug-14834/bug-14834.c index 3a16a2d222..af2070d303 100644 --- a/ext/-test-/bug-14834/bug-14384.c +++ b/ext/-test-/bug-14834/bug-14834.c @@ -7,7 +7,7 @@ static NOINLINE(VALUE f(VALUE)); static NOINLINE(void g(VALUE, void*)); -extern NOINLINE(void Init_bug_14384(void)); +extern NOINLINE(void Init_bug_14834(void)); void Init_bug_14834(void) diff --git a/ext/-test-/bug-14834/depend b/ext/-test-/bug-14834/depend index bf26a571aa..f83939d559 100644 --- a/ext/-test-/bug-14834/depend +++ b/ext/-test-/bug-14834/depend @@ -1,161 +1,163 @@ # AUTOGENERATED DEPENDENCIES START -bug-14384.o: $(RUBY_EXTCONF_H) -bug-14384.o: $(arch_hdrdir)/ruby/config.h -bug-14384.o: $(hdrdir)/ruby/assert.h -bug-14384.o: $(hdrdir)/ruby/backward.h -bug-14384.o: $(hdrdir)/ruby/backward/2/assume.h -bug-14384.o: $(hdrdir)/ruby/backward/2/attributes.h -bug-14384.o: $(hdrdir)/ruby/backward/2/bool.h -bug-14384.o: $(hdrdir)/ruby/backward/2/inttypes.h -bug-14384.o: $(hdrdir)/ruby/backward/2/limits.h -bug-14384.o: $(hdrdir)/ruby/backward/2/long_long.h -bug-14384.o: $(hdrdir)/ruby/backward/2/stdalign.h -bug-14384.o: $(hdrdir)/ruby/backward/2/stdarg.h -bug-14384.o: $(hdrdir)/ruby/debug.h -bug-14384.o: $(hdrdir)/ruby/defines.h -bug-14384.o: $(hdrdir)/ruby/intern.h -bug-14384.o: $(hdrdir)/ruby/internal/abi.h -bug-14384.o: $(hdrdir)/ruby/internal/anyargs.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/char.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/double.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/int.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/long.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/short.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h -bug-14384.o: $(hdrdir)/ruby/internal/assume.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/alloc_size.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/artificial.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/cold.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/const.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/constexpr.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/deprecated.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/error.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/flag_enum.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/forceinline.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/format.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/noalias.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/nodiscard.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/noexcept.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/noinline.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/nonnull.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/noreturn.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/packed_struct.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/pure.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/restrict.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/warning.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/weakref.h -bug-14384.o: $(hdrdir)/ruby/internal/cast.h -bug-14384.o: $(hdrdir)/ruby/internal/compiler_is.h -bug-14384.o: $(hdrdir)/ruby/internal/compiler_is/apple.h -bug-14384.o: $(hdrdir)/ruby/internal/compiler_is/clang.h -bug-14384.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h -bug-14384.o: $(hdrdir)/ruby/internal/compiler_is/intel.h -bug-14384.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h -bug-14384.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h -bug-14384.o: $(hdrdir)/ruby/internal/compiler_since.h -bug-14384.o: $(hdrdir)/ruby/internal/config.h -bug-14384.o: $(hdrdir)/ruby/internal/constant_p.h -bug-14384.o: $(hdrdir)/ruby/internal/core.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rarray.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rbasic.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rbignum.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rclass.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rdata.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rfile.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rhash.h -bug-14384.o: $(hdrdir)/ruby/internal/core/robject.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rregexp.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rstring.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rstruct.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rtypeddata.h -bug-14384.o: $(hdrdir)/ruby/internal/ctype.h -bug-14384.o: $(hdrdir)/ruby/internal/dllexport.h -bug-14384.o: $(hdrdir)/ruby/internal/dosish.h -bug-14384.o: $(hdrdir)/ruby/internal/error.h -bug-14384.o: $(hdrdir)/ruby/internal/eval.h -bug-14384.o: $(hdrdir)/ruby/internal/event.h -bug-14384.o: $(hdrdir)/ruby/internal/fl_type.h -bug-14384.o: $(hdrdir)/ruby/internal/gc.h -bug-14384.o: $(hdrdir)/ruby/internal/glob.h -bug-14384.o: $(hdrdir)/ruby/internal/globals.h -bug-14384.o: $(hdrdir)/ruby/internal/has/attribute.h -bug-14384.o: $(hdrdir)/ruby/internal/has/builtin.h -bug-14384.o: $(hdrdir)/ruby/internal/has/c_attribute.h -bug-14384.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h -bug-14384.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h -bug-14384.o: $(hdrdir)/ruby/internal/has/extension.h -bug-14384.o: $(hdrdir)/ruby/internal/has/feature.h -bug-14384.o: $(hdrdir)/ruby/internal/has/warning.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/array.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/bignum.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/class.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/compar.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/complex.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/cont.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/dir.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/enum.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/enumerator.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/error.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/eval.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/file.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/hash.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/io.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/load.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/marshal.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/numeric.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/object.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/parse.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/proc.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/process.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/random.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/range.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/rational.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/re.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/ruby.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/select.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/select/largesize.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/signal.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/sprintf.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/string.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/struct.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/thread.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/time.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/variable.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/vm.h -bug-14384.o: $(hdrdir)/ruby/internal/interpreter.h -bug-14384.o: $(hdrdir)/ruby/internal/iterator.h -bug-14384.o: $(hdrdir)/ruby/internal/memory.h -bug-14384.o: $(hdrdir)/ruby/internal/method.h -bug-14384.o: $(hdrdir)/ruby/internal/module.h -bug-14384.o: $(hdrdir)/ruby/internal/newobj.h -bug-14384.o: $(hdrdir)/ruby/internal/scan_args.h -bug-14384.o: $(hdrdir)/ruby/internal/special_consts.h -bug-14384.o: $(hdrdir)/ruby/internal/static_assert.h -bug-14384.o: $(hdrdir)/ruby/internal/stdalign.h -bug-14384.o: $(hdrdir)/ruby/internal/stdbool.h -bug-14384.o: $(hdrdir)/ruby/internal/symbol.h -bug-14384.o: $(hdrdir)/ruby/internal/value.h -bug-14384.o: $(hdrdir)/ruby/internal/value_type.h -bug-14384.o: $(hdrdir)/ruby/internal/variable.h -bug-14384.o: $(hdrdir)/ruby/internal/warning_push.h -bug-14384.o: $(hdrdir)/ruby/internal/xmalloc.h -bug-14384.o: $(hdrdir)/ruby/missing.h -bug-14384.o: $(hdrdir)/ruby/ruby.h -bug-14384.o: $(hdrdir)/ruby/st.h -bug-14384.o: $(hdrdir)/ruby/subst.h -bug-14384.o: bug-14384.c +bug-14834.o: $(RUBY_EXTCONF_H) +bug-14834.o: $(arch_hdrdir)/ruby/config.h +bug-14834.o: $(hdrdir)/ruby/assert.h +bug-14834.o: $(hdrdir)/ruby/backward.h +bug-14834.o: $(hdrdir)/ruby/backward/2/assume.h +bug-14834.o: $(hdrdir)/ruby/backward/2/attributes.h +bug-14834.o: $(hdrdir)/ruby/backward/2/bool.h +bug-14834.o: $(hdrdir)/ruby/backward/2/inttypes.h +bug-14834.o: $(hdrdir)/ruby/backward/2/limits.h +bug-14834.o: $(hdrdir)/ruby/backward/2/long_long.h +bug-14834.o: $(hdrdir)/ruby/backward/2/stdalign.h +bug-14834.o: $(hdrdir)/ruby/backward/2/stdarg.h +bug-14834.o: $(hdrdir)/ruby/debug.h +bug-14834.o: $(hdrdir)/ruby/defines.h +bug-14834.o: $(hdrdir)/ruby/intern.h +bug-14834.o: $(hdrdir)/ruby/internal/abi.h +bug-14834.o: $(hdrdir)/ruby/internal/anyargs.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/char.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/double.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/int.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/long.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/short.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +bug-14834.o: $(hdrdir)/ruby/internal/assume.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/artificial.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/cold.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/const.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/constexpr.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/deprecated.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/error.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/forceinline.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/format.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/noalias.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/noexcept.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/noinline.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/nonnull.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/noreturn.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/pure.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/restrict.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/warning.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/weakref.h +bug-14834.o: $(hdrdir)/ruby/internal/cast.h +bug-14834.o: $(hdrdir)/ruby/internal/compiler_is.h +bug-14834.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +bug-14834.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +bug-14834.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +bug-14834.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +bug-14834.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +bug-14834.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +bug-14834.o: $(hdrdir)/ruby/internal/compiler_since.h +bug-14834.o: $(hdrdir)/ruby/internal/config.h +bug-14834.o: $(hdrdir)/ruby/internal/constant_p.h +bug-14834.o: $(hdrdir)/ruby/internal/core.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rarray.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rbasic.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rbignum.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rclass.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rdata.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rfile.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rhash.h +bug-14834.o: $(hdrdir)/ruby/internal/core/robject.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rregexp.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rstring.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rstruct.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +bug-14834.o: $(hdrdir)/ruby/internal/ctype.h +bug-14834.o: $(hdrdir)/ruby/internal/dllexport.h +bug-14834.o: $(hdrdir)/ruby/internal/dosish.h +bug-14834.o: $(hdrdir)/ruby/internal/error.h +bug-14834.o: $(hdrdir)/ruby/internal/eval.h +bug-14834.o: $(hdrdir)/ruby/internal/event.h +bug-14834.o: $(hdrdir)/ruby/internal/fl_type.h +bug-14834.o: $(hdrdir)/ruby/internal/gc.h +bug-14834.o: $(hdrdir)/ruby/internal/glob.h +bug-14834.o: $(hdrdir)/ruby/internal/globals.h +bug-14834.o: $(hdrdir)/ruby/internal/has/attribute.h +bug-14834.o: $(hdrdir)/ruby/internal/has/builtin.h +bug-14834.o: $(hdrdir)/ruby/internal/has/c_attribute.h +bug-14834.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +bug-14834.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +bug-14834.o: $(hdrdir)/ruby/internal/has/extension.h +bug-14834.o: $(hdrdir)/ruby/internal/has/feature.h +bug-14834.o: $(hdrdir)/ruby/internal/has/warning.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/array.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/bignum.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/class.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/compar.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/complex.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/cont.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/dir.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/enum.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/enumerator.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/error.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/eval.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/file.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/hash.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/io.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/load.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/marshal.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/numeric.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/object.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/parse.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/proc.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/process.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/random.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/range.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/rational.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/re.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/ruby.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/select.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/set.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/signal.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/sprintf.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/string.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/struct.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/thread.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/time.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/variable.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/vm.h +bug-14834.o: $(hdrdir)/ruby/internal/interpreter.h +bug-14834.o: $(hdrdir)/ruby/internal/iterator.h +bug-14834.o: $(hdrdir)/ruby/internal/memory.h +bug-14834.o: $(hdrdir)/ruby/internal/method.h +bug-14834.o: $(hdrdir)/ruby/internal/module.h +bug-14834.o: $(hdrdir)/ruby/internal/newobj.h +bug-14834.o: $(hdrdir)/ruby/internal/scan_args.h +bug-14834.o: $(hdrdir)/ruby/internal/special_consts.h +bug-14834.o: $(hdrdir)/ruby/internal/static_assert.h +bug-14834.o: $(hdrdir)/ruby/internal/stdalign.h +bug-14834.o: $(hdrdir)/ruby/internal/stdbool.h +bug-14834.o: $(hdrdir)/ruby/internal/stdckdint.h +bug-14834.o: $(hdrdir)/ruby/internal/symbol.h +bug-14834.o: $(hdrdir)/ruby/internal/value.h +bug-14834.o: $(hdrdir)/ruby/internal/value_type.h +bug-14834.o: $(hdrdir)/ruby/internal/variable.h +bug-14834.o: $(hdrdir)/ruby/internal/warning_push.h +bug-14834.o: $(hdrdir)/ruby/internal/xmalloc.h +bug-14834.o: $(hdrdir)/ruby/missing.h +bug-14834.o: $(hdrdir)/ruby/ruby.h +bug-14834.o: $(hdrdir)/ruby/st.h +bug-14834.o: $(hdrdir)/ruby/subst.h +bug-14834.o: bug-14834.c # AUTOGENERATED DEPENDENCIES END diff --git a/ext/-test-/bug-3571/depend b/ext/-test-/bug-3571/depend index 9105093b0d..69c970b6f2 100644 --- a/ext/-test-/bug-3571/depend +++ b/ext/-test-/bug-3571/depend @@ -128,6 +128,7 @@ bug.o: $(hdrdir)/ruby/internal/intern/re.h bug.o: $(hdrdir)/ruby/internal/intern/ruby.h bug.o: $(hdrdir)/ruby/internal/intern/select.h bug.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +bug.o: $(hdrdir)/ruby/internal/intern/set.h bug.o: $(hdrdir)/ruby/internal/intern/signal.h bug.o: $(hdrdir)/ruby/internal/intern/sprintf.h bug.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ bug.o: $(hdrdir)/ruby/internal/special_consts.h bug.o: $(hdrdir)/ruby/internal/static_assert.h bug.o: $(hdrdir)/ruby/internal/stdalign.h bug.o: $(hdrdir)/ruby/internal/stdbool.h +bug.o: $(hdrdir)/ruby/internal/stdckdint.h bug.o: $(hdrdir)/ruby/internal/symbol.h bug.o: $(hdrdir)/ruby/internal/value.h bug.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/bug-5832/depend b/ext/-test-/bug-5832/depend index 9105093b0d..69c970b6f2 100644 --- a/ext/-test-/bug-5832/depend +++ b/ext/-test-/bug-5832/depend @@ -128,6 +128,7 @@ bug.o: $(hdrdir)/ruby/internal/intern/re.h bug.o: $(hdrdir)/ruby/internal/intern/ruby.h bug.o: $(hdrdir)/ruby/internal/intern/select.h bug.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +bug.o: $(hdrdir)/ruby/internal/intern/set.h bug.o: $(hdrdir)/ruby/internal/intern/signal.h bug.o: $(hdrdir)/ruby/internal/intern/sprintf.h bug.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ bug.o: $(hdrdir)/ruby/internal/special_consts.h bug.o: $(hdrdir)/ruby/internal/static_assert.h bug.o: $(hdrdir)/ruby/internal/stdalign.h bug.o: $(hdrdir)/ruby/internal/stdbool.h +bug.o: $(hdrdir)/ruby/internal/stdckdint.h bug.o: $(hdrdir)/ruby/internal/symbol.h bug.o: $(hdrdir)/ruby/internal/value.h bug.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/bug_reporter/depend b/ext/-test-/bug_reporter/depend index 20882708d1..e9993c3295 100644 --- a/ext/-test-/bug_reporter/depend +++ b/ext/-test-/bug_reporter/depend @@ -128,6 +128,7 @@ bug_reporter.o: $(hdrdir)/ruby/internal/intern/re.h bug_reporter.o: $(hdrdir)/ruby/internal/intern/ruby.h bug_reporter.o: $(hdrdir)/ruby/internal/intern/select.h bug_reporter.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +bug_reporter.o: $(hdrdir)/ruby/internal/intern/set.h bug_reporter.o: $(hdrdir)/ruby/internal/intern/signal.h bug_reporter.o: $(hdrdir)/ruby/internal/intern/sprintf.h bug_reporter.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ bug_reporter.o: $(hdrdir)/ruby/internal/special_consts.h bug_reporter.o: $(hdrdir)/ruby/internal/static_assert.h bug_reporter.o: $(hdrdir)/ruby/internal/stdalign.h bug_reporter.o: $(hdrdir)/ruby/internal/stdbool.h +bug_reporter.o: $(hdrdir)/ruby/internal/stdckdint.h bug_reporter.o: $(hdrdir)/ruby/internal/symbol.h bug_reporter.o: $(hdrdir)/ruby/internal/value.h bug_reporter.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/class/depend b/ext/-test-/class/depend index 0a805f815e..557206cefb 100644 --- a/ext/-test-/class/depend +++ b/ext/-test-/class/depend @@ -127,6 +127,7 @@ class2name.o: $(hdrdir)/ruby/internal/intern/re.h class2name.o: $(hdrdir)/ruby/internal/intern/ruby.h class2name.o: $(hdrdir)/ruby/internal/intern/select.h class2name.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +class2name.o: $(hdrdir)/ruby/internal/intern/set.h class2name.o: $(hdrdir)/ruby/internal/intern/signal.h class2name.o: $(hdrdir)/ruby/internal/intern/sprintf.h class2name.o: $(hdrdir)/ruby/internal/intern/string.h @@ -146,6 +147,7 @@ class2name.o: $(hdrdir)/ruby/internal/special_consts.h class2name.o: $(hdrdir)/ruby/internal/static_assert.h class2name.o: $(hdrdir)/ruby/internal/stdalign.h class2name.o: $(hdrdir)/ruby/internal/stdbool.h +class2name.o: $(hdrdir)/ruby/internal/stdckdint.h class2name.o: $(hdrdir)/ruby/internal/symbol.h class2name.o: $(hdrdir)/ruby/internal/value.h class2name.o: $(hdrdir)/ruby/internal/value_type.h @@ -286,6 +288,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -305,6 +308,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/class/init.c b/ext/-test-/class/init.c index ed715c1942..108ff7525c 100644 --- a/ext/-test-/class/init.c +++ b/ext/-test-/class/init.c @@ -7,5 +7,6 @@ Init_class(void) { VALUE mBug = rb_define_module("Bug"); VALUE mod = rb_define_module_under(mBug, "Class"); + rb_define_class_under(mod, "TestClassDefinedInC", rb_cObject); TEST_INIT_FUNCS(init); } diff --git a/ext/-test-/cxxanyargs/cxxanyargs.cpp b/ext/-test-/cxxanyargs/cxxanyargs.cpp index eded13e2ee..c7df7f9038 100644 --- a/ext/-test-/cxxanyargs/cxxanyargs.cpp +++ b/ext/-test-/cxxanyargs/cxxanyargs.cpp @@ -97,31 +97,6 @@ struct test_rb_define_hooked_variable { }; VALUE test_rb_define_hooked_variable::v = Qundef; -namespace test_rb_iterate { - VALUE - iter(VALUE self) - { - return rb_funcall(self, rb_intern("yield"), 0); - } - - VALUE - block(RB_BLOCK_CALL_FUNC_ARGLIST(arg, param)) - { - return rb_funcall(arg, rb_intern("=="), 1, param); - } - - VALUE - test(VALUE self) - { -#ifdef HAVE_NULLPTR - rb_iterate(iter, self, nullptr, self); -#endif - - rb_iterate(iter, self, RUBY_METHOD_FUNC(block), self); // old - return rb_iterate(iter, self, block, self); // new - } -} - namespace test_rb_block_call { VALUE block(RB_BLOCK_CALL_FUNC_ARGLIST(arg, param)) @@ -936,7 +911,6 @@ Init_cxxanyargs(void) test(rb_define_virtual_variable); test(rb_define_hooked_variable); - test(rb_iterate); test(rb_block_call); test(rb_rescue); test(rb_rescue2); diff --git a/ext/-test-/debug/depend b/ext/-test-/debug/depend index 5feeea6d98..4ae0378ef2 100644 --- a/ext/-test-/debug/depend +++ b/ext/-test-/debug/depend @@ -128,6 +128,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -287,6 +289,7 @@ inspector.o: $(hdrdir)/ruby/internal/intern/re.h inspector.o: $(hdrdir)/ruby/internal/intern/ruby.h inspector.o: $(hdrdir)/ruby/internal/intern/select.h inspector.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +inspector.o: $(hdrdir)/ruby/internal/intern/set.h inspector.o: $(hdrdir)/ruby/internal/intern/signal.h inspector.o: $(hdrdir)/ruby/internal/intern/sprintf.h inspector.o: $(hdrdir)/ruby/internal/intern/string.h @@ -306,6 +309,7 @@ inspector.o: $(hdrdir)/ruby/internal/special_consts.h inspector.o: $(hdrdir)/ruby/internal/static_assert.h inspector.o: $(hdrdir)/ruby/internal/stdalign.h inspector.o: $(hdrdir)/ruby/internal/stdbool.h +inspector.o: $(hdrdir)/ruby/internal/stdckdint.h inspector.o: $(hdrdir)/ruby/internal/symbol.h inspector.o: $(hdrdir)/ruby/internal/value.h inspector.o: $(hdrdir)/ruby/internal/value_type.h @@ -446,6 +450,7 @@ profile_frames.o: $(hdrdir)/ruby/internal/intern/re.h profile_frames.o: $(hdrdir)/ruby/internal/intern/ruby.h profile_frames.o: $(hdrdir)/ruby/internal/intern/select.h profile_frames.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +profile_frames.o: $(hdrdir)/ruby/internal/intern/set.h profile_frames.o: $(hdrdir)/ruby/internal/intern/signal.h profile_frames.o: $(hdrdir)/ruby/internal/intern/sprintf.h profile_frames.o: $(hdrdir)/ruby/internal/intern/string.h @@ -465,6 +470,7 @@ profile_frames.o: $(hdrdir)/ruby/internal/special_consts.h profile_frames.o: $(hdrdir)/ruby/internal/static_assert.h profile_frames.o: $(hdrdir)/ruby/internal/stdalign.h profile_frames.o: $(hdrdir)/ruby/internal/stdbool.h +profile_frames.o: $(hdrdir)/ruby/internal/stdckdint.h profile_frames.o: $(hdrdir)/ruby/internal/symbol.h profile_frames.o: $(hdrdir)/ruby/internal/value.h profile_frames.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/dln/empty/depend b/ext/-test-/dln/empty/depend index a460159087..58f1508598 100644 --- a/ext/-test-/dln/empty/depend +++ b/ext/-test-/dln/empty/depend @@ -128,6 +128,7 @@ empty.o: $(hdrdir)/ruby/internal/intern/re.h empty.o: $(hdrdir)/ruby/internal/intern/ruby.h empty.o: $(hdrdir)/ruby/internal/intern/select.h empty.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +empty.o: $(hdrdir)/ruby/internal/intern/set.h empty.o: $(hdrdir)/ruby/internal/intern/signal.h empty.o: $(hdrdir)/ruby/internal/intern/sprintf.h empty.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ empty.o: $(hdrdir)/ruby/internal/special_consts.h empty.o: $(hdrdir)/ruby/internal/static_assert.h empty.o: $(hdrdir)/ruby/internal/stdalign.h empty.o: $(hdrdir)/ruby/internal/stdbool.h +empty.o: $(hdrdir)/ruby/internal/stdckdint.h empty.o: $(hdrdir)/ruby/internal/symbol.h empty.o: $(hdrdir)/ruby/internal/value.h empty.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/econv/append.c b/ext/-test-/econv/append.c index 724cd136c0..eb473c47a3 100644 --- a/ext/-test-/econv/append.c +++ b/ext/-test-/econv/append.c @@ -5,6 +5,8 @@ static VALUE econv_append(VALUE self, VALUE src, VALUE dst) { rb_econv_t *ec = DATA_PTR(self); + StringValue(src); + StringValue(dst); return rb_econv_str_append(ec, src, dst, 0); } diff --git a/ext/-test-/econv/depend b/ext/-test-/econv/depend new file mode 100644 index 0000000000..3a5bc9c659 --- /dev/null +++ b/ext/-test-/econv/depend @@ -0,0 +1,336 @@ +# AUTOGENERATED DEPENDENCIES START +append.o: $(RUBY_EXTCONF_H) +append.o: $(arch_hdrdir)/ruby/config.h +append.o: $(hdrdir)/ruby.h +append.o: $(hdrdir)/ruby/assert.h +append.o: $(hdrdir)/ruby/backward.h +append.o: $(hdrdir)/ruby/backward/2/assume.h +append.o: $(hdrdir)/ruby/backward/2/attributes.h +append.o: $(hdrdir)/ruby/backward/2/bool.h +append.o: $(hdrdir)/ruby/backward/2/inttypes.h +append.o: $(hdrdir)/ruby/backward/2/limits.h +append.o: $(hdrdir)/ruby/backward/2/long_long.h +append.o: $(hdrdir)/ruby/backward/2/stdalign.h +append.o: $(hdrdir)/ruby/backward/2/stdarg.h +append.o: $(hdrdir)/ruby/defines.h +append.o: $(hdrdir)/ruby/encoding.h +append.o: $(hdrdir)/ruby/intern.h +append.o: $(hdrdir)/ruby/internal/abi.h +append.o: $(hdrdir)/ruby/internal/anyargs.h +append.o: $(hdrdir)/ruby/internal/arithmetic.h +append.o: $(hdrdir)/ruby/internal/arithmetic/char.h +append.o: $(hdrdir)/ruby/internal/arithmetic/double.h +append.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +append.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +append.o: $(hdrdir)/ruby/internal/arithmetic/int.h +append.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +append.o: $(hdrdir)/ruby/internal/arithmetic/long.h +append.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +append.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +append.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +append.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +append.o: $(hdrdir)/ruby/internal/arithmetic/short.h +append.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +append.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +append.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +append.o: $(hdrdir)/ruby/internal/assume.h +append.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +append.o: $(hdrdir)/ruby/internal/attr/artificial.h +append.o: $(hdrdir)/ruby/internal/attr/cold.h +append.o: $(hdrdir)/ruby/internal/attr/const.h +append.o: $(hdrdir)/ruby/internal/attr/constexpr.h +append.o: $(hdrdir)/ruby/internal/attr/deprecated.h +append.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +append.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +append.o: $(hdrdir)/ruby/internal/attr/error.h +append.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +append.o: $(hdrdir)/ruby/internal/attr/forceinline.h +append.o: $(hdrdir)/ruby/internal/attr/format.h +append.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +append.o: $(hdrdir)/ruby/internal/attr/noalias.h +append.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +append.o: $(hdrdir)/ruby/internal/attr/noexcept.h +append.o: $(hdrdir)/ruby/internal/attr/noinline.h +append.o: $(hdrdir)/ruby/internal/attr/nonnull.h +append.o: $(hdrdir)/ruby/internal/attr/noreturn.h +append.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +append.o: $(hdrdir)/ruby/internal/attr/pure.h +append.o: $(hdrdir)/ruby/internal/attr/restrict.h +append.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +append.o: $(hdrdir)/ruby/internal/attr/warning.h +append.o: $(hdrdir)/ruby/internal/attr/weakref.h +append.o: $(hdrdir)/ruby/internal/cast.h +append.o: $(hdrdir)/ruby/internal/compiler_is.h +append.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +append.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +append.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +append.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +append.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +append.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +append.o: $(hdrdir)/ruby/internal/compiler_since.h +append.o: $(hdrdir)/ruby/internal/config.h +append.o: $(hdrdir)/ruby/internal/constant_p.h +append.o: $(hdrdir)/ruby/internal/core.h +append.o: $(hdrdir)/ruby/internal/core/rarray.h +append.o: $(hdrdir)/ruby/internal/core/rbasic.h +append.o: $(hdrdir)/ruby/internal/core/rbignum.h +append.o: $(hdrdir)/ruby/internal/core/rclass.h +append.o: $(hdrdir)/ruby/internal/core/rdata.h +append.o: $(hdrdir)/ruby/internal/core/rfile.h +append.o: $(hdrdir)/ruby/internal/core/rhash.h +append.o: $(hdrdir)/ruby/internal/core/robject.h +append.o: $(hdrdir)/ruby/internal/core/rregexp.h +append.o: $(hdrdir)/ruby/internal/core/rstring.h +append.o: $(hdrdir)/ruby/internal/core/rstruct.h +append.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +append.o: $(hdrdir)/ruby/internal/ctype.h +append.o: $(hdrdir)/ruby/internal/dllexport.h +append.o: $(hdrdir)/ruby/internal/dosish.h +append.o: $(hdrdir)/ruby/internal/encoding/coderange.h +append.o: $(hdrdir)/ruby/internal/encoding/ctype.h +append.o: $(hdrdir)/ruby/internal/encoding/encoding.h +append.o: $(hdrdir)/ruby/internal/encoding/pathname.h +append.o: $(hdrdir)/ruby/internal/encoding/re.h +append.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +append.o: $(hdrdir)/ruby/internal/encoding/string.h +append.o: $(hdrdir)/ruby/internal/encoding/symbol.h +append.o: $(hdrdir)/ruby/internal/encoding/transcode.h +append.o: $(hdrdir)/ruby/internal/error.h +append.o: $(hdrdir)/ruby/internal/eval.h +append.o: $(hdrdir)/ruby/internal/event.h +append.o: $(hdrdir)/ruby/internal/fl_type.h +append.o: $(hdrdir)/ruby/internal/gc.h +append.o: $(hdrdir)/ruby/internal/glob.h +append.o: $(hdrdir)/ruby/internal/globals.h +append.o: $(hdrdir)/ruby/internal/has/attribute.h +append.o: $(hdrdir)/ruby/internal/has/builtin.h +append.o: $(hdrdir)/ruby/internal/has/c_attribute.h +append.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +append.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +append.o: $(hdrdir)/ruby/internal/has/extension.h +append.o: $(hdrdir)/ruby/internal/has/feature.h +append.o: $(hdrdir)/ruby/internal/has/warning.h +append.o: $(hdrdir)/ruby/internal/intern/array.h +append.o: $(hdrdir)/ruby/internal/intern/bignum.h +append.o: $(hdrdir)/ruby/internal/intern/class.h +append.o: $(hdrdir)/ruby/internal/intern/compar.h +append.o: $(hdrdir)/ruby/internal/intern/complex.h +append.o: $(hdrdir)/ruby/internal/intern/cont.h +append.o: $(hdrdir)/ruby/internal/intern/dir.h +append.o: $(hdrdir)/ruby/internal/intern/enum.h +append.o: $(hdrdir)/ruby/internal/intern/enumerator.h +append.o: $(hdrdir)/ruby/internal/intern/error.h +append.o: $(hdrdir)/ruby/internal/intern/eval.h +append.o: $(hdrdir)/ruby/internal/intern/file.h +append.o: $(hdrdir)/ruby/internal/intern/hash.h +append.o: $(hdrdir)/ruby/internal/intern/io.h +append.o: $(hdrdir)/ruby/internal/intern/load.h +append.o: $(hdrdir)/ruby/internal/intern/marshal.h +append.o: $(hdrdir)/ruby/internal/intern/numeric.h +append.o: $(hdrdir)/ruby/internal/intern/object.h +append.o: $(hdrdir)/ruby/internal/intern/parse.h +append.o: $(hdrdir)/ruby/internal/intern/proc.h +append.o: $(hdrdir)/ruby/internal/intern/process.h +append.o: $(hdrdir)/ruby/internal/intern/random.h +append.o: $(hdrdir)/ruby/internal/intern/range.h +append.o: $(hdrdir)/ruby/internal/intern/rational.h +append.o: $(hdrdir)/ruby/internal/intern/re.h +append.o: $(hdrdir)/ruby/internal/intern/ruby.h +append.o: $(hdrdir)/ruby/internal/intern/select.h +append.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +append.o: $(hdrdir)/ruby/internal/intern/set.h +append.o: $(hdrdir)/ruby/internal/intern/signal.h +append.o: $(hdrdir)/ruby/internal/intern/sprintf.h +append.o: $(hdrdir)/ruby/internal/intern/string.h +append.o: $(hdrdir)/ruby/internal/intern/struct.h +append.o: $(hdrdir)/ruby/internal/intern/thread.h +append.o: $(hdrdir)/ruby/internal/intern/time.h +append.o: $(hdrdir)/ruby/internal/intern/variable.h +append.o: $(hdrdir)/ruby/internal/intern/vm.h +append.o: $(hdrdir)/ruby/internal/interpreter.h +append.o: $(hdrdir)/ruby/internal/iterator.h +append.o: $(hdrdir)/ruby/internal/memory.h +append.o: $(hdrdir)/ruby/internal/method.h +append.o: $(hdrdir)/ruby/internal/module.h +append.o: $(hdrdir)/ruby/internal/newobj.h +append.o: $(hdrdir)/ruby/internal/scan_args.h +append.o: $(hdrdir)/ruby/internal/special_consts.h +append.o: $(hdrdir)/ruby/internal/static_assert.h +append.o: $(hdrdir)/ruby/internal/stdalign.h +append.o: $(hdrdir)/ruby/internal/stdbool.h +append.o: $(hdrdir)/ruby/internal/stdckdint.h +append.o: $(hdrdir)/ruby/internal/symbol.h +append.o: $(hdrdir)/ruby/internal/value.h +append.o: $(hdrdir)/ruby/internal/value_type.h +append.o: $(hdrdir)/ruby/internal/variable.h +append.o: $(hdrdir)/ruby/internal/warning_push.h +append.o: $(hdrdir)/ruby/internal/xmalloc.h +append.o: $(hdrdir)/ruby/missing.h +append.o: $(hdrdir)/ruby/onigmo.h +append.o: $(hdrdir)/ruby/oniguruma.h +append.o: $(hdrdir)/ruby/ruby.h +append.o: $(hdrdir)/ruby/st.h +append.o: $(hdrdir)/ruby/subst.h +append.o: append.c +init.o: $(RUBY_EXTCONF_H) +init.o: $(arch_hdrdir)/ruby/config.h +init.o: $(hdrdir)/ruby.h +init.o: $(hdrdir)/ruby/assert.h +init.o: $(hdrdir)/ruby/backward.h +init.o: $(hdrdir)/ruby/backward/2/assume.h +init.o: $(hdrdir)/ruby/backward/2/attributes.h +init.o: $(hdrdir)/ruby/backward/2/bool.h +init.o: $(hdrdir)/ruby/backward/2/inttypes.h +init.o: $(hdrdir)/ruby/backward/2/limits.h +init.o: $(hdrdir)/ruby/backward/2/long_long.h +init.o: $(hdrdir)/ruby/backward/2/stdalign.h +init.o: $(hdrdir)/ruby/backward/2/stdarg.h +init.o: $(hdrdir)/ruby/defines.h +init.o: $(hdrdir)/ruby/intern.h +init.o: $(hdrdir)/ruby/internal/abi.h +init.o: $(hdrdir)/ruby/internal/anyargs.h +init.o: $(hdrdir)/ruby/internal/arithmetic.h +init.o: $(hdrdir)/ruby/internal/arithmetic/char.h +init.o: $(hdrdir)/ruby/internal/arithmetic/double.h +init.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +init.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/int.h +init.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/long.h +init.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +init.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/short.h +init.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +init.o: $(hdrdir)/ruby/internal/assume.h +init.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +init.o: $(hdrdir)/ruby/internal/attr/artificial.h +init.o: $(hdrdir)/ruby/internal/attr/cold.h +init.o: $(hdrdir)/ruby/internal/attr/const.h +init.o: $(hdrdir)/ruby/internal/attr/constexpr.h +init.o: $(hdrdir)/ruby/internal/attr/deprecated.h +init.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +init.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +init.o: $(hdrdir)/ruby/internal/attr/error.h +init.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +init.o: $(hdrdir)/ruby/internal/attr/forceinline.h +init.o: $(hdrdir)/ruby/internal/attr/format.h +init.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +init.o: $(hdrdir)/ruby/internal/attr/noalias.h +init.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +init.o: $(hdrdir)/ruby/internal/attr/noexcept.h +init.o: $(hdrdir)/ruby/internal/attr/noinline.h +init.o: $(hdrdir)/ruby/internal/attr/nonnull.h +init.o: $(hdrdir)/ruby/internal/attr/noreturn.h +init.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +init.o: $(hdrdir)/ruby/internal/attr/pure.h +init.o: $(hdrdir)/ruby/internal/attr/restrict.h +init.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +init.o: $(hdrdir)/ruby/internal/attr/warning.h +init.o: $(hdrdir)/ruby/internal/attr/weakref.h +init.o: $(hdrdir)/ruby/internal/cast.h +init.o: $(hdrdir)/ruby/internal/compiler_is.h +init.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +init.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +init.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +init.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +init.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +init.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +init.o: $(hdrdir)/ruby/internal/compiler_since.h +init.o: $(hdrdir)/ruby/internal/config.h +init.o: $(hdrdir)/ruby/internal/constant_p.h +init.o: $(hdrdir)/ruby/internal/core.h +init.o: $(hdrdir)/ruby/internal/core/rarray.h +init.o: $(hdrdir)/ruby/internal/core/rbasic.h +init.o: $(hdrdir)/ruby/internal/core/rbignum.h +init.o: $(hdrdir)/ruby/internal/core/rclass.h +init.o: $(hdrdir)/ruby/internal/core/rdata.h +init.o: $(hdrdir)/ruby/internal/core/rfile.h +init.o: $(hdrdir)/ruby/internal/core/rhash.h +init.o: $(hdrdir)/ruby/internal/core/robject.h +init.o: $(hdrdir)/ruby/internal/core/rregexp.h +init.o: $(hdrdir)/ruby/internal/core/rstring.h +init.o: $(hdrdir)/ruby/internal/core/rstruct.h +init.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +init.o: $(hdrdir)/ruby/internal/ctype.h +init.o: $(hdrdir)/ruby/internal/dllexport.h +init.o: $(hdrdir)/ruby/internal/dosish.h +init.o: $(hdrdir)/ruby/internal/error.h +init.o: $(hdrdir)/ruby/internal/eval.h +init.o: $(hdrdir)/ruby/internal/event.h +init.o: $(hdrdir)/ruby/internal/fl_type.h +init.o: $(hdrdir)/ruby/internal/gc.h +init.o: $(hdrdir)/ruby/internal/glob.h +init.o: $(hdrdir)/ruby/internal/globals.h +init.o: $(hdrdir)/ruby/internal/has/attribute.h +init.o: $(hdrdir)/ruby/internal/has/builtin.h +init.o: $(hdrdir)/ruby/internal/has/c_attribute.h +init.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +init.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +init.o: $(hdrdir)/ruby/internal/has/extension.h +init.o: $(hdrdir)/ruby/internal/has/feature.h +init.o: $(hdrdir)/ruby/internal/has/warning.h +init.o: $(hdrdir)/ruby/internal/intern/array.h +init.o: $(hdrdir)/ruby/internal/intern/bignum.h +init.o: $(hdrdir)/ruby/internal/intern/class.h +init.o: $(hdrdir)/ruby/internal/intern/compar.h +init.o: $(hdrdir)/ruby/internal/intern/complex.h +init.o: $(hdrdir)/ruby/internal/intern/cont.h +init.o: $(hdrdir)/ruby/internal/intern/dir.h +init.o: $(hdrdir)/ruby/internal/intern/enum.h +init.o: $(hdrdir)/ruby/internal/intern/enumerator.h +init.o: $(hdrdir)/ruby/internal/intern/error.h +init.o: $(hdrdir)/ruby/internal/intern/eval.h +init.o: $(hdrdir)/ruby/internal/intern/file.h +init.o: $(hdrdir)/ruby/internal/intern/hash.h +init.o: $(hdrdir)/ruby/internal/intern/io.h +init.o: $(hdrdir)/ruby/internal/intern/load.h +init.o: $(hdrdir)/ruby/internal/intern/marshal.h +init.o: $(hdrdir)/ruby/internal/intern/numeric.h +init.o: $(hdrdir)/ruby/internal/intern/object.h +init.o: $(hdrdir)/ruby/internal/intern/parse.h +init.o: $(hdrdir)/ruby/internal/intern/proc.h +init.o: $(hdrdir)/ruby/internal/intern/process.h +init.o: $(hdrdir)/ruby/internal/intern/random.h +init.o: $(hdrdir)/ruby/internal/intern/range.h +init.o: $(hdrdir)/ruby/internal/intern/rational.h +init.o: $(hdrdir)/ruby/internal/intern/re.h +init.o: $(hdrdir)/ruby/internal/intern/ruby.h +init.o: $(hdrdir)/ruby/internal/intern/select.h +init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h +init.o: $(hdrdir)/ruby/internal/intern/signal.h +init.o: $(hdrdir)/ruby/internal/intern/sprintf.h +init.o: $(hdrdir)/ruby/internal/intern/string.h +init.o: $(hdrdir)/ruby/internal/intern/struct.h +init.o: $(hdrdir)/ruby/internal/intern/thread.h +init.o: $(hdrdir)/ruby/internal/intern/time.h +init.o: $(hdrdir)/ruby/internal/intern/variable.h +init.o: $(hdrdir)/ruby/internal/intern/vm.h +init.o: $(hdrdir)/ruby/internal/interpreter.h +init.o: $(hdrdir)/ruby/internal/iterator.h +init.o: $(hdrdir)/ruby/internal/memory.h +init.o: $(hdrdir)/ruby/internal/method.h +init.o: $(hdrdir)/ruby/internal/module.h +init.o: $(hdrdir)/ruby/internal/newobj.h +init.o: $(hdrdir)/ruby/internal/scan_args.h +init.o: $(hdrdir)/ruby/internal/special_consts.h +init.o: $(hdrdir)/ruby/internal/static_assert.h +init.o: $(hdrdir)/ruby/internal/stdalign.h +init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h +init.o: $(hdrdir)/ruby/internal/symbol.h +init.o: $(hdrdir)/ruby/internal/value.h +init.o: $(hdrdir)/ruby/internal/value_type.h +init.o: $(hdrdir)/ruby/internal/variable.h +init.o: $(hdrdir)/ruby/internal/warning_push.h +init.o: $(hdrdir)/ruby/internal/xmalloc.h +init.o: $(hdrdir)/ruby/missing.h +init.o: $(hdrdir)/ruby/ruby.h +init.o: $(hdrdir)/ruby/st.h +init.o: $(hdrdir)/ruby/subst.h +init.o: init.c +# AUTOGENERATED DEPENDENCIES END diff --git a/ext/-test-/ensure_and_callcc/depend b/ext/-test-/ensure_and_callcc/depend new file mode 100644 index 0000000000..54431847a6 --- /dev/null +++ b/ext/-test-/ensure_and_callcc/depend @@ -0,0 +1,163 @@ +# AUTOGENERATED DEPENDENCIES START +ensure_and_callcc.o: $(RUBY_EXTCONF_H) +ensure_and_callcc.o: $(arch_hdrdir)/ruby/config.h +ensure_and_callcc.o: $(hdrdir)/ruby.h +ensure_and_callcc.o: $(hdrdir)/ruby/assert.h +ensure_and_callcc.o: $(hdrdir)/ruby/backward.h +ensure_and_callcc.o: $(hdrdir)/ruby/backward/2/assume.h +ensure_and_callcc.o: $(hdrdir)/ruby/backward/2/attributes.h +ensure_and_callcc.o: $(hdrdir)/ruby/backward/2/bool.h +ensure_and_callcc.o: $(hdrdir)/ruby/backward/2/inttypes.h +ensure_and_callcc.o: $(hdrdir)/ruby/backward/2/limits.h +ensure_and_callcc.o: $(hdrdir)/ruby/backward/2/long_long.h +ensure_and_callcc.o: $(hdrdir)/ruby/backward/2/stdalign.h +ensure_and_callcc.o: $(hdrdir)/ruby/backward/2/stdarg.h +ensure_and_callcc.o: $(hdrdir)/ruby/defines.h +ensure_and_callcc.o: $(hdrdir)/ruby/intern.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/abi.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/anyargs.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/arithmetic.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/arithmetic/char.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/arithmetic/double.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/arithmetic/int.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/arithmetic/long.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/arithmetic/short.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/assume.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/artificial.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/cold.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/const.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/constexpr.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/deprecated.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/error.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/forceinline.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/format.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/noalias.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/noexcept.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/noinline.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/nonnull.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/noreturn.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/pure.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/restrict.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/warning.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/attr/weakref.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/cast.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/compiler_is.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/compiler_since.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/config.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/constant_p.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/core.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/core/rarray.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/core/rbasic.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/core/rbignum.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/core/rclass.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/core/rdata.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/core/rfile.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/core/rhash.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/core/robject.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/core/rregexp.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/core/rstring.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/core/rstruct.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/ctype.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/dllexport.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/dosish.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/error.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/eval.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/event.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/fl_type.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/gc.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/glob.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/globals.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/has/attribute.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/has/builtin.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/has/c_attribute.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/has/extension.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/has/feature.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/has/warning.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/array.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/bignum.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/class.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/compar.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/complex.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/cont.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/dir.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/enum.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/enumerator.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/error.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/eval.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/file.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/hash.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/io.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/load.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/marshal.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/numeric.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/object.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/parse.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/proc.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/process.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/random.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/range.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/rational.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/re.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/ruby.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/select.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/set.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/signal.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/sprintf.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/string.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/struct.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/thread.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/time.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/variable.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/intern/vm.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/interpreter.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/iterator.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/memory.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/method.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/module.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/newobj.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/scan_args.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/special_consts.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/static_assert.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/stdalign.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/stdbool.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/stdckdint.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/symbol.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/value.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/value_type.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/variable.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/warning_push.h +ensure_and_callcc.o: $(hdrdir)/ruby/internal/xmalloc.h +ensure_and_callcc.o: $(hdrdir)/ruby/missing.h +ensure_and_callcc.o: $(hdrdir)/ruby/ruby.h +ensure_and_callcc.o: $(hdrdir)/ruby/st.h +ensure_and_callcc.o: $(hdrdir)/ruby/subst.h +ensure_and_callcc.o: ensure_and_callcc.c +# AUTOGENERATED DEPENDENCIES END diff --git a/ext/-test-/ensure_and_callcc/ensure_and_callcc.c b/ext/-test-/ensure_and_callcc/ensure_and_callcc.c new file mode 100644 index 0000000000..1a92de69c3 --- /dev/null +++ b/ext/-test-/ensure_and_callcc/ensure_and_callcc.c @@ -0,0 +1,58 @@ +#include "ruby.h" + +static VALUE rb_mEnsureAndCallcc; + +struct require_data { + VALUE obj; + VALUE fname; +}; + +static VALUE +call_require(VALUE arg) +{ + struct require_data *data = (struct require_data *)arg; + rb_f_require(data->obj, data->fname); + return Qnil; +} + +static VALUE +call_ensure(VALUE _) +{ + VALUE v = rb_iv_get(rb_mEnsureAndCallcc, "@ensure_called"); + int called = FIX2INT(v) + 1; + rb_iv_set(rb_mEnsureAndCallcc, "@ensure_called", INT2FIX(called)); + return Qnil; +} + +static VALUE +require_with_ensure(VALUE self, VALUE fname) +{ + struct require_data data = { + .obj = self, + .fname = fname + }; + return rb_ensure(call_require, (VALUE)&data, call_ensure, Qnil); +} + +static VALUE +ensure_called(VALUE self) +{ + return rb_iv_get(rb_mEnsureAndCallcc, "@ensure_called"); +} + +static VALUE +reset(VALUE self) +{ + rb_iv_set(rb_mEnsureAndCallcc, "@ensure_called", INT2FIX(0)); + return Qnil; +} + +void +Init_ensure_and_callcc(void) +{ + rb_mEnsureAndCallcc = rb_define_module("EnsureAndCallcc"); + rb_iv_set(rb_mEnsureAndCallcc, "@ensure_called", INT2FIX(0)); + rb_define_singleton_method(rb_mEnsureAndCallcc, "reset", reset, 0); + rb_define_singleton_method(rb_mEnsureAndCallcc, "ensure_called", ensure_called, 0); + rb_define_singleton_method(rb_mEnsureAndCallcc, "require_with_ensure", require_with_ensure, 1); +} diff --git a/ext/-test-/ensure_and_callcc/extconf.rb b/ext/-test-/ensure_and_callcc/extconf.rb new file mode 100644 index 0000000000..123b80b8d0 --- /dev/null +++ b/ext/-test-/ensure_and_callcc/extconf.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: false +require "mkmf" + +require_relative "../auto_ext.rb" +auto_ext(inc: true) diff --git a/ext/-test-/enumerator_kw/depend b/ext/-test-/enumerator_kw/depend index 49ea495421..b6d2f0a998 100644 --- a/ext/-test-/enumerator_kw/depend +++ b/ext/-test-/enumerator_kw/depend @@ -128,6 +128,7 @@ enumerator_kw.o: $(hdrdir)/ruby/internal/intern/re.h enumerator_kw.o: $(hdrdir)/ruby/internal/intern/ruby.h enumerator_kw.o: $(hdrdir)/ruby/internal/intern/select.h enumerator_kw.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +enumerator_kw.o: $(hdrdir)/ruby/internal/intern/set.h enumerator_kw.o: $(hdrdir)/ruby/internal/intern/signal.h enumerator_kw.o: $(hdrdir)/ruby/internal/intern/sprintf.h enumerator_kw.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ enumerator_kw.o: $(hdrdir)/ruby/internal/special_consts.h enumerator_kw.o: $(hdrdir)/ruby/internal/static_assert.h enumerator_kw.o: $(hdrdir)/ruby/internal/stdalign.h enumerator_kw.o: $(hdrdir)/ruby/internal/stdbool.h +enumerator_kw.o: $(hdrdir)/ruby/internal/stdckdint.h enumerator_kw.o: $(hdrdir)/ruby/internal/symbol.h enumerator_kw.o: $(hdrdir)/ruby/internal/value.h enumerator_kw.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/eval/depend b/ext/-test-/eval/depend new file mode 100644 index 0000000000..03a1c7d7ef --- /dev/null +++ b/ext/-test-/eval/depend @@ -0,0 +1,162 @@ +# AUTOGENERATED DEPENDENCIES START +eval.o: $(RUBY_EXTCONF_H) +eval.o: $(arch_hdrdir)/ruby/config.h +eval.o: $(hdrdir)/ruby/assert.h +eval.o: $(hdrdir)/ruby/backward.h +eval.o: $(hdrdir)/ruby/backward/2/assume.h +eval.o: $(hdrdir)/ruby/backward/2/attributes.h +eval.o: $(hdrdir)/ruby/backward/2/bool.h +eval.o: $(hdrdir)/ruby/backward/2/inttypes.h +eval.o: $(hdrdir)/ruby/backward/2/limits.h +eval.o: $(hdrdir)/ruby/backward/2/long_long.h +eval.o: $(hdrdir)/ruby/backward/2/stdalign.h +eval.o: $(hdrdir)/ruby/backward/2/stdarg.h +eval.o: $(hdrdir)/ruby/defines.h +eval.o: $(hdrdir)/ruby/intern.h +eval.o: $(hdrdir)/ruby/internal/abi.h +eval.o: $(hdrdir)/ruby/internal/anyargs.h +eval.o: $(hdrdir)/ruby/internal/arithmetic.h +eval.o: $(hdrdir)/ruby/internal/arithmetic/char.h +eval.o: $(hdrdir)/ruby/internal/arithmetic/double.h +eval.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +eval.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +eval.o: $(hdrdir)/ruby/internal/arithmetic/int.h +eval.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +eval.o: $(hdrdir)/ruby/internal/arithmetic/long.h +eval.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +eval.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +eval.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +eval.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +eval.o: $(hdrdir)/ruby/internal/arithmetic/short.h +eval.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +eval.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +eval.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +eval.o: $(hdrdir)/ruby/internal/assume.h +eval.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +eval.o: $(hdrdir)/ruby/internal/attr/artificial.h +eval.o: $(hdrdir)/ruby/internal/attr/cold.h +eval.o: $(hdrdir)/ruby/internal/attr/const.h +eval.o: $(hdrdir)/ruby/internal/attr/constexpr.h +eval.o: $(hdrdir)/ruby/internal/attr/deprecated.h +eval.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +eval.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +eval.o: $(hdrdir)/ruby/internal/attr/error.h +eval.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +eval.o: $(hdrdir)/ruby/internal/attr/forceinline.h +eval.o: $(hdrdir)/ruby/internal/attr/format.h +eval.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +eval.o: $(hdrdir)/ruby/internal/attr/noalias.h +eval.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +eval.o: $(hdrdir)/ruby/internal/attr/noexcept.h +eval.o: $(hdrdir)/ruby/internal/attr/noinline.h +eval.o: $(hdrdir)/ruby/internal/attr/nonnull.h +eval.o: $(hdrdir)/ruby/internal/attr/noreturn.h +eval.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +eval.o: $(hdrdir)/ruby/internal/attr/pure.h +eval.o: $(hdrdir)/ruby/internal/attr/restrict.h +eval.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +eval.o: $(hdrdir)/ruby/internal/attr/warning.h +eval.o: $(hdrdir)/ruby/internal/attr/weakref.h +eval.o: $(hdrdir)/ruby/internal/cast.h +eval.o: $(hdrdir)/ruby/internal/compiler_is.h +eval.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +eval.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +eval.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +eval.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +eval.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +eval.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +eval.o: $(hdrdir)/ruby/internal/compiler_since.h +eval.o: $(hdrdir)/ruby/internal/config.h +eval.o: $(hdrdir)/ruby/internal/constant_p.h +eval.o: $(hdrdir)/ruby/internal/core.h +eval.o: $(hdrdir)/ruby/internal/core/rarray.h +eval.o: $(hdrdir)/ruby/internal/core/rbasic.h +eval.o: $(hdrdir)/ruby/internal/core/rbignum.h +eval.o: $(hdrdir)/ruby/internal/core/rclass.h +eval.o: $(hdrdir)/ruby/internal/core/rdata.h +eval.o: $(hdrdir)/ruby/internal/core/rfile.h +eval.o: $(hdrdir)/ruby/internal/core/rhash.h +eval.o: $(hdrdir)/ruby/internal/core/robject.h +eval.o: $(hdrdir)/ruby/internal/core/rregexp.h +eval.o: $(hdrdir)/ruby/internal/core/rstring.h +eval.o: $(hdrdir)/ruby/internal/core/rstruct.h +eval.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +eval.o: $(hdrdir)/ruby/internal/ctype.h +eval.o: $(hdrdir)/ruby/internal/dllexport.h +eval.o: $(hdrdir)/ruby/internal/dosish.h +eval.o: $(hdrdir)/ruby/internal/error.h +eval.o: $(hdrdir)/ruby/internal/eval.h +eval.o: $(hdrdir)/ruby/internal/event.h +eval.o: $(hdrdir)/ruby/internal/fl_type.h +eval.o: $(hdrdir)/ruby/internal/gc.h +eval.o: $(hdrdir)/ruby/internal/glob.h +eval.o: $(hdrdir)/ruby/internal/globals.h +eval.o: $(hdrdir)/ruby/internal/has/attribute.h +eval.o: $(hdrdir)/ruby/internal/has/builtin.h +eval.o: $(hdrdir)/ruby/internal/has/c_attribute.h +eval.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +eval.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +eval.o: $(hdrdir)/ruby/internal/has/extension.h +eval.o: $(hdrdir)/ruby/internal/has/feature.h +eval.o: $(hdrdir)/ruby/internal/has/warning.h +eval.o: $(hdrdir)/ruby/internal/intern/array.h +eval.o: $(hdrdir)/ruby/internal/intern/bignum.h +eval.o: $(hdrdir)/ruby/internal/intern/class.h +eval.o: $(hdrdir)/ruby/internal/intern/compar.h +eval.o: $(hdrdir)/ruby/internal/intern/complex.h +eval.o: $(hdrdir)/ruby/internal/intern/cont.h +eval.o: $(hdrdir)/ruby/internal/intern/dir.h +eval.o: $(hdrdir)/ruby/internal/intern/enum.h +eval.o: $(hdrdir)/ruby/internal/intern/enumerator.h +eval.o: $(hdrdir)/ruby/internal/intern/error.h +eval.o: $(hdrdir)/ruby/internal/intern/eval.h +eval.o: $(hdrdir)/ruby/internal/intern/file.h +eval.o: $(hdrdir)/ruby/internal/intern/hash.h +eval.o: $(hdrdir)/ruby/internal/intern/io.h +eval.o: $(hdrdir)/ruby/internal/intern/load.h +eval.o: $(hdrdir)/ruby/internal/intern/marshal.h +eval.o: $(hdrdir)/ruby/internal/intern/numeric.h +eval.o: $(hdrdir)/ruby/internal/intern/object.h +eval.o: $(hdrdir)/ruby/internal/intern/parse.h +eval.o: $(hdrdir)/ruby/internal/intern/proc.h +eval.o: $(hdrdir)/ruby/internal/intern/process.h +eval.o: $(hdrdir)/ruby/internal/intern/random.h +eval.o: $(hdrdir)/ruby/internal/intern/range.h +eval.o: $(hdrdir)/ruby/internal/intern/rational.h +eval.o: $(hdrdir)/ruby/internal/intern/re.h +eval.o: $(hdrdir)/ruby/internal/intern/ruby.h +eval.o: $(hdrdir)/ruby/internal/intern/select.h +eval.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +eval.o: $(hdrdir)/ruby/internal/intern/set.h +eval.o: $(hdrdir)/ruby/internal/intern/signal.h +eval.o: $(hdrdir)/ruby/internal/intern/sprintf.h +eval.o: $(hdrdir)/ruby/internal/intern/string.h +eval.o: $(hdrdir)/ruby/internal/intern/struct.h +eval.o: $(hdrdir)/ruby/internal/intern/thread.h +eval.o: $(hdrdir)/ruby/internal/intern/time.h +eval.o: $(hdrdir)/ruby/internal/intern/variable.h +eval.o: $(hdrdir)/ruby/internal/intern/vm.h +eval.o: $(hdrdir)/ruby/internal/interpreter.h +eval.o: $(hdrdir)/ruby/internal/iterator.h +eval.o: $(hdrdir)/ruby/internal/memory.h +eval.o: $(hdrdir)/ruby/internal/method.h +eval.o: $(hdrdir)/ruby/internal/module.h +eval.o: $(hdrdir)/ruby/internal/newobj.h +eval.o: $(hdrdir)/ruby/internal/scan_args.h +eval.o: $(hdrdir)/ruby/internal/special_consts.h +eval.o: $(hdrdir)/ruby/internal/static_assert.h +eval.o: $(hdrdir)/ruby/internal/stdalign.h +eval.o: $(hdrdir)/ruby/internal/stdbool.h +eval.o: $(hdrdir)/ruby/internal/stdckdint.h +eval.o: $(hdrdir)/ruby/internal/symbol.h +eval.o: $(hdrdir)/ruby/internal/value.h +eval.o: $(hdrdir)/ruby/internal/value_type.h +eval.o: $(hdrdir)/ruby/internal/variable.h +eval.o: $(hdrdir)/ruby/internal/warning_push.h +eval.o: $(hdrdir)/ruby/internal/xmalloc.h +eval.o: $(hdrdir)/ruby/missing.h +eval.o: $(hdrdir)/ruby/ruby.h +eval.o: $(hdrdir)/ruby/st.h +eval.o: $(hdrdir)/ruby/subst.h +eval.o: eval.c +# AUTOGENERATED DEPENDENCIES END diff --git a/ext/-test-/exception/depend b/ext/-test-/exception/depend index 818b4c79df..690e5ad377 100644 --- a/ext/-test-/exception/depend +++ b/ext/-test-/exception/depend @@ -127,6 +127,7 @@ dataerror.o: $(hdrdir)/ruby/internal/intern/re.h dataerror.o: $(hdrdir)/ruby/internal/intern/ruby.h dataerror.o: $(hdrdir)/ruby/internal/intern/select.h dataerror.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +dataerror.o: $(hdrdir)/ruby/internal/intern/set.h dataerror.o: $(hdrdir)/ruby/internal/intern/signal.h dataerror.o: $(hdrdir)/ruby/internal/intern/sprintf.h dataerror.o: $(hdrdir)/ruby/internal/intern/string.h @@ -146,6 +147,7 @@ dataerror.o: $(hdrdir)/ruby/internal/special_consts.h dataerror.o: $(hdrdir)/ruby/internal/static_assert.h dataerror.o: $(hdrdir)/ruby/internal/stdalign.h dataerror.o: $(hdrdir)/ruby/internal/stdbool.h +dataerror.o: $(hdrdir)/ruby/internal/stdckdint.h dataerror.o: $(hdrdir)/ruby/internal/symbol.h dataerror.o: $(hdrdir)/ruby/internal/value.h dataerror.o: $(hdrdir)/ruby/internal/value_type.h @@ -296,6 +298,7 @@ enc_raise.o: $(hdrdir)/ruby/internal/intern/re.h enc_raise.o: $(hdrdir)/ruby/internal/intern/ruby.h enc_raise.o: $(hdrdir)/ruby/internal/intern/select.h enc_raise.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +enc_raise.o: $(hdrdir)/ruby/internal/intern/set.h enc_raise.o: $(hdrdir)/ruby/internal/intern/signal.h enc_raise.o: $(hdrdir)/ruby/internal/intern/sprintf.h enc_raise.o: $(hdrdir)/ruby/internal/intern/string.h @@ -315,6 +318,7 @@ enc_raise.o: $(hdrdir)/ruby/internal/special_consts.h enc_raise.o: $(hdrdir)/ruby/internal/static_assert.h enc_raise.o: $(hdrdir)/ruby/internal/stdalign.h enc_raise.o: $(hdrdir)/ruby/internal/stdbool.h +enc_raise.o: $(hdrdir)/ruby/internal/stdckdint.h enc_raise.o: $(hdrdir)/ruby/internal/symbol.h enc_raise.o: $(hdrdir)/ruby/internal/value.h enc_raise.o: $(hdrdir)/ruby/internal/value_type.h @@ -457,6 +461,7 @@ ensured.o: $(hdrdir)/ruby/internal/intern/re.h ensured.o: $(hdrdir)/ruby/internal/intern/ruby.h ensured.o: $(hdrdir)/ruby/internal/intern/select.h ensured.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ensured.o: $(hdrdir)/ruby/internal/intern/set.h ensured.o: $(hdrdir)/ruby/internal/intern/signal.h ensured.o: $(hdrdir)/ruby/internal/intern/sprintf.h ensured.o: $(hdrdir)/ruby/internal/intern/string.h @@ -476,6 +481,7 @@ ensured.o: $(hdrdir)/ruby/internal/special_consts.h ensured.o: $(hdrdir)/ruby/internal/static_assert.h ensured.o: $(hdrdir)/ruby/internal/stdalign.h ensured.o: $(hdrdir)/ruby/internal/stdbool.h +ensured.o: $(hdrdir)/ruby/internal/stdckdint.h ensured.o: $(hdrdir)/ruby/internal/symbol.h ensured.o: $(hdrdir)/ruby/internal/value.h ensured.o: $(hdrdir)/ruby/internal/value_type.h @@ -616,6 +622,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -635,6 +642,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/fatal/depend b/ext/-test-/fatal/depend index 730a72e52a..306bc9099c 100644 --- a/ext/-test-/fatal/depend +++ b/ext/-test-/fatal/depend @@ -1,4 +1,327 @@ # AUTOGENERATED DEPENDENCIES START + +init.o: $(RUBY_EXTCONF_H) +init.o: $(arch_hdrdir)/ruby/config.h +init.o: $(hdrdir)/ruby.h +init.o: $(hdrdir)/ruby/assert.h +init.o: $(hdrdir)/ruby/backward.h +init.o: $(hdrdir)/ruby/backward/2/assume.h +init.o: $(hdrdir)/ruby/backward/2/attributes.h +init.o: $(hdrdir)/ruby/backward/2/bool.h +init.o: $(hdrdir)/ruby/backward/2/inttypes.h +init.o: $(hdrdir)/ruby/backward/2/limits.h +init.o: $(hdrdir)/ruby/backward/2/long_long.h +init.o: $(hdrdir)/ruby/backward/2/stdalign.h +init.o: $(hdrdir)/ruby/backward/2/stdarg.h +init.o: $(hdrdir)/ruby/defines.h +init.o: $(hdrdir)/ruby/intern.h +init.o: $(hdrdir)/ruby/internal/abi.h +init.o: $(hdrdir)/ruby/internal/anyargs.h +init.o: $(hdrdir)/ruby/internal/arithmetic.h +init.o: $(hdrdir)/ruby/internal/arithmetic/char.h +init.o: $(hdrdir)/ruby/internal/arithmetic/double.h +init.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +init.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/int.h +init.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/long.h +init.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +init.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/short.h +init.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +init.o: $(hdrdir)/ruby/internal/assume.h +init.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +init.o: $(hdrdir)/ruby/internal/attr/artificial.h +init.o: $(hdrdir)/ruby/internal/attr/cold.h +init.o: $(hdrdir)/ruby/internal/attr/const.h +init.o: $(hdrdir)/ruby/internal/attr/constexpr.h +init.o: $(hdrdir)/ruby/internal/attr/deprecated.h +init.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +init.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +init.o: $(hdrdir)/ruby/internal/attr/error.h +init.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +init.o: $(hdrdir)/ruby/internal/attr/forceinline.h +init.o: $(hdrdir)/ruby/internal/attr/format.h +init.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +init.o: $(hdrdir)/ruby/internal/attr/noalias.h +init.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +init.o: $(hdrdir)/ruby/internal/attr/noexcept.h +init.o: $(hdrdir)/ruby/internal/attr/noinline.h +init.o: $(hdrdir)/ruby/internal/attr/nonnull.h +init.o: $(hdrdir)/ruby/internal/attr/noreturn.h +init.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +init.o: $(hdrdir)/ruby/internal/attr/pure.h +init.o: $(hdrdir)/ruby/internal/attr/restrict.h +init.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +init.o: $(hdrdir)/ruby/internal/attr/warning.h +init.o: $(hdrdir)/ruby/internal/attr/weakref.h +init.o: $(hdrdir)/ruby/internal/cast.h +init.o: $(hdrdir)/ruby/internal/compiler_is.h +init.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +init.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +init.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +init.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +init.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +init.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +init.o: $(hdrdir)/ruby/internal/compiler_since.h +init.o: $(hdrdir)/ruby/internal/config.h +init.o: $(hdrdir)/ruby/internal/constant_p.h +init.o: $(hdrdir)/ruby/internal/core.h +init.o: $(hdrdir)/ruby/internal/core/rarray.h +init.o: $(hdrdir)/ruby/internal/core/rbasic.h +init.o: $(hdrdir)/ruby/internal/core/rbignum.h +init.o: $(hdrdir)/ruby/internal/core/rclass.h +init.o: $(hdrdir)/ruby/internal/core/rdata.h +init.o: $(hdrdir)/ruby/internal/core/rfile.h +init.o: $(hdrdir)/ruby/internal/core/rhash.h +init.o: $(hdrdir)/ruby/internal/core/robject.h +init.o: $(hdrdir)/ruby/internal/core/rregexp.h +init.o: $(hdrdir)/ruby/internal/core/rstring.h +init.o: $(hdrdir)/ruby/internal/core/rstruct.h +init.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +init.o: $(hdrdir)/ruby/internal/ctype.h +init.o: $(hdrdir)/ruby/internal/dllexport.h +init.o: $(hdrdir)/ruby/internal/dosish.h +init.o: $(hdrdir)/ruby/internal/error.h +init.o: $(hdrdir)/ruby/internal/eval.h +init.o: $(hdrdir)/ruby/internal/event.h +init.o: $(hdrdir)/ruby/internal/fl_type.h +init.o: $(hdrdir)/ruby/internal/gc.h +init.o: $(hdrdir)/ruby/internal/glob.h +init.o: $(hdrdir)/ruby/internal/globals.h +init.o: $(hdrdir)/ruby/internal/has/attribute.h +init.o: $(hdrdir)/ruby/internal/has/builtin.h +init.o: $(hdrdir)/ruby/internal/has/c_attribute.h +init.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +init.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +init.o: $(hdrdir)/ruby/internal/has/extension.h +init.o: $(hdrdir)/ruby/internal/has/feature.h +init.o: $(hdrdir)/ruby/internal/has/warning.h +init.o: $(hdrdir)/ruby/internal/intern/array.h +init.o: $(hdrdir)/ruby/internal/intern/bignum.h +init.o: $(hdrdir)/ruby/internal/intern/class.h +init.o: $(hdrdir)/ruby/internal/intern/compar.h +init.o: $(hdrdir)/ruby/internal/intern/complex.h +init.o: $(hdrdir)/ruby/internal/intern/cont.h +init.o: $(hdrdir)/ruby/internal/intern/dir.h +init.o: $(hdrdir)/ruby/internal/intern/enum.h +init.o: $(hdrdir)/ruby/internal/intern/enumerator.h +init.o: $(hdrdir)/ruby/internal/intern/error.h +init.o: $(hdrdir)/ruby/internal/intern/eval.h +init.o: $(hdrdir)/ruby/internal/intern/file.h +init.o: $(hdrdir)/ruby/internal/intern/hash.h +init.o: $(hdrdir)/ruby/internal/intern/io.h +init.o: $(hdrdir)/ruby/internal/intern/load.h +init.o: $(hdrdir)/ruby/internal/intern/marshal.h +init.o: $(hdrdir)/ruby/internal/intern/numeric.h +init.o: $(hdrdir)/ruby/internal/intern/object.h +init.o: $(hdrdir)/ruby/internal/intern/parse.h +init.o: $(hdrdir)/ruby/internal/intern/proc.h +init.o: $(hdrdir)/ruby/internal/intern/process.h +init.o: $(hdrdir)/ruby/internal/intern/random.h +init.o: $(hdrdir)/ruby/internal/intern/range.h +init.o: $(hdrdir)/ruby/internal/intern/rational.h +init.o: $(hdrdir)/ruby/internal/intern/re.h +init.o: $(hdrdir)/ruby/internal/intern/ruby.h +init.o: $(hdrdir)/ruby/internal/intern/select.h +init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h +init.o: $(hdrdir)/ruby/internal/intern/signal.h +init.o: $(hdrdir)/ruby/internal/intern/sprintf.h +init.o: $(hdrdir)/ruby/internal/intern/string.h +init.o: $(hdrdir)/ruby/internal/intern/struct.h +init.o: $(hdrdir)/ruby/internal/intern/thread.h +init.o: $(hdrdir)/ruby/internal/intern/time.h +init.o: $(hdrdir)/ruby/internal/intern/variable.h +init.o: $(hdrdir)/ruby/internal/intern/vm.h +init.o: $(hdrdir)/ruby/internal/interpreter.h +init.o: $(hdrdir)/ruby/internal/iterator.h +init.o: $(hdrdir)/ruby/internal/memory.h +init.o: $(hdrdir)/ruby/internal/method.h +init.o: $(hdrdir)/ruby/internal/module.h +init.o: $(hdrdir)/ruby/internal/newobj.h +init.o: $(hdrdir)/ruby/internal/scan_args.h +init.o: $(hdrdir)/ruby/internal/special_consts.h +init.o: $(hdrdir)/ruby/internal/static_assert.h +init.o: $(hdrdir)/ruby/internal/stdalign.h +init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h +init.o: $(hdrdir)/ruby/internal/symbol.h +init.o: $(hdrdir)/ruby/internal/value.h +init.o: $(hdrdir)/ruby/internal/value_type.h +init.o: $(hdrdir)/ruby/internal/variable.h +init.o: $(hdrdir)/ruby/internal/warning_push.h +init.o: $(hdrdir)/ruby/internal/xmalloc.h +init.o: $(hdrdir)/ruby/missing.h +init.o: $(hdrdir)/ruby/ruby.h +init.o: $(hdrdir)/ruby/st.h +init.o: $(hdrdir)/ruby/subst.h +init.o: init.c +invalid.o: $(RUBY_EXTCONF_H) +invalid.o: $(arch_hdrdir)/ruby/config.h +invalid.o: $(hdrdir)/ruby.h +invalid.o: $(hdrdir)/ruby/assert.h +invalid.o: $(hdrdir)/ruby/backward.h +invalid.o: $(hdrdir)/ruby/backward/2/assume.h +invalid.o: $(hdrdir)/ruby/backward/2/attributes.h +invalid.o: $(hdrdir)/ruby/backward/2/bool.h +invalid.o: $(hdrdir)/ruby/backward/2/inttypes.h +invalid.o: $(hdrdir)/ruby/backward/2/limits.h +invalid.o: $(hdrdir)/ruby/backward/2/long_long.h +invalid.o: $(hdrdir)/ruby/backward/2/stdalign.h +invalid.o: $(hdrdir)/ruby/backward/2/stdarg.h +invalid.o: $(hdrdir)/ruby/defines.h +invalid.o: $(hdrdir)/ruby/intern.h +invalid.o: $(hdrdir)/ruby/internal/abi.h +invalid.o: $(hdrdir)/ruby/internal/anyargs.h +invalid.o: $(hdrdir)/ruby/internal/arithmetic.h +invalid.o: $(hdrdir)/ruby/internal/arithmetic/char.h +invalid.o: $(hdrdir)/ruby/internal/arithmetic/double.h +invalid.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +invalid.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +invalid.o: $(hdrdir)/ruby/internal/arithmetic/int.h +invalid.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +invalid.o: $(hdrdir)/ruby/internal/arithmetic/long.h +invalid.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +invalid.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +invalid.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +invalid.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +invalid.o: $(hdrdir)/ruby/internal/arithmetic/short.h +invalid.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +invalid.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +invalid.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +invalid.o: $(hdrdir)/ruby/internal/assume.h +invalid.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +invalid.o: $(hdrdir)/ruby/internal/attr/artificial.h +invalid.o: $(hdrdir)/ruby/internal/attr/cold.h +invalid.o: $(hdrdir)/ruby/internal/attr/const.h +invalid.o: $(hdrdir)/ruby/internal/attr/constexpr.h +invalid.o: $(hdrdir)/ruby/internal/attr/deprecated.h +invalid.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +invalid.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +invalid.o: $(hdrdir)/ruby/internal/attr/error.h +invalid.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +invalid.o: $(hdrdir)/ruby/internal/attr/forceinline.h +invalid.o: $(hdrdir)/ruby/internal/attr/format.h +invalid.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +invalid.o: $(hdrdir)/ruby/internal/attr/noalias.h +invalid.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +invalid.o: $(hdrdir)/ruby/internal/attr/noexcept.h +invalid.o: $(hdrdir)/ruby/internal/attr/noinline.h +invalid.o: $(hdrdir)/ruby/internal/attr/nonnull.h +invalid.o: $(hdrdir)/ruby/internal/attr/noreturn.h +invalid.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +invalid.o: $(hdrdir)/ruby/internal/attr/pure.h +invalid.o: $(hdrdir)/ruby/internal/attr/restrict.h +invalid.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +invalid.o: $(hdrdir)/ruby/internal/attr/warning.h +invalid.o: $(hdrdir)/ruby/internal/attr/weakref.h +invalid.o: $(hdrdir)/ruby/internal/cast.h +invalid.o: $(hdrdir)/ruby/internal/compiler_is.h +invalid.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +invalid.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +invalid.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +invalid.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +invalid.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +invalid.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +invalid.o: $(hdrdir)/ruby/internal/compiler_since.h +invalid.o: $(hdrdir)/ruby/internal/config.h +invalid.o: $(hdrdir)/ruby/internal/constant_p.h +invalid.o: $(hdrdir)/ruby/internal/core.h +invalid.o: $(hdrdir)/ruby/internal/core/rarray.h +invalid.o: $(hdrdir)/ruby/internal/core/rbasic.h +invalid.o: $(hdrdir)/ruby/internal/core/rbignum.h +invalid.o: $(hdrdir)/ruby/internal/core/rclass.h +invalid.o: $(hdrdir)/ruby/internal/core/rdata.h +invalid.o: $(hdrdir)/ruby/internal/core/rfile.h +invalid.o: $(hdrdir)/ruby/internal/core/rhash.h +invalid.o: $(hdrdir)/ruby/internal/core/robject.h +invalid.o: $(hdrdir)/ruby/internal/core/rregexp.h +invalid.o: $(hdrdir)/ruby/internal/core/rstring.h +invalid.o: $(hdrdir)/ruby/internal/core/rstruct.h +invalid.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +invalid.o: $(hdrdir)/ruby/internal/ctype.h +invalid.o: $(hdrdir)/ruby/internal/dllexport.h +invalid.o: $(hdrdir)/ruby/internal/dosish.h +invalid.o: $(hdrdir)/ruby/internal/error.h +invalid.o: $(hdrdir)/ruby/internal/eval.h +invalid.o: $(hdrdir)/ruby/internal/event.h +invalid.o: $(hdrdir)/ruby/internal/fl_type.h +invalid.o: $(hdrdir)/ruby/internal/gc.h +invalid.o: $(hdrdir)/ruby/internal/glob.h +invalid.o: $(hdrdir)/ruby/internal/globals.h +invalid.o: $(hdrdir)/ruby/internal/has/attribute.h +invalid.o: $(hdrdir)/ruby/internal/has/builtin.h +invalid.o: $(hdrdir)/ruby/internal/has/c_attribute.h +invalid.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +invalid.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +invalid.o: $(hdrdir)/ruby/internal/has/extension.h +invalid.o: $(hdrdir)/ruby/internal/has/feature.h +invalid.o: $(hdrdir)/ruby/internal/has/warning.h +invalid.o: $(hdrdir)/ruby/internal/intern/array.h +invalid.o: $(hdrdir)/ruby/internal/intern/bignum.h +invalid.o: $(hdrdir)/ruby/internal/intern/class.h +invalid.o: $(hdrdir)/ruby/internal/intern/compar.h +invalid.o: $(hdrdir)/ruby/internal/intern/complex.h +invalid.o: $(hdrdir)/ruby/internal/intern/cont.h +invalid.o: $(hdrdir)/ruby/internal/intern/dir.h +invalid.o: $(hdrdir)/ruby/internal/intern/enum.h +invalid.o: $(hdrdir)/ruby/internal/intern/enumerator.h +invalid.o: $(hdrdir)/ruby/internal/intern/error.h +invalid.o: $(hdrdir)/ruby/internal/intern/eval.h +invalid.o: $(hdrdir)/ruby/internal/intern/file.h +invalid.o: $(hdrdir)/ruby/internal/intern/hash.h +invalid.o: $(hdrdir)/ruby/internal/intern/io.h +invalid.o: $(hdrdir)/ruby/internal/intern/load.h +invalid.o: $(hdrdir)/ruby/internal/intern/marshal.h +invalid.o: $(hdrdir)/ruby/internal/intern/numeric.h +invalid.o: $(hdrdir)/ruby/internal/intern/object.h +invalid.o: $(hdrdir)/ruby/internal/intern/parse.h +invalid.o: $(hdrdir)/ruby/internal/intern/proc.h +invalid.o: $(hdrdir)/ruby/internal/intern/process.h +invalid.o: $(hdrdir)/ruby/internal/intern/random.h +invalid.o: $(hdrdir)/ruby/internal/intern/range.h +invalid.o: $(hdrdir)/ruby/internal/intern/rational.h +invalid.o: $(hdrdir)/ruby/internal/intern/re.h +invalid.o: $(hdrdir)/ruby/internal/intern/ruby.h +invalid.o: $(hdrdir)/ruby/internal/intern/select.h +invalid.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +invalid.o: $(hdrdir)/ruby/internal/intern/set.h +invalid.o: $(hdrdir)/ruby/internal/intern/signal.h +invalid.o: $(hdrdir)/ruby/internal/intern/sprintf.h +invalid.o: $(hdrdir)/ruby/internal/intern/string.h +invalid.o: $(hdrdir)/ruby/internal/intern/struct.h +invalid.o: $(hdrdir)/ruby/internal/intern/thread.h +invalid.o: $(hdrdir)/ruby/internal/intern/time.h +invalid.o: $(hdrdir)/ruby/internal/intern/variable.h +invalid.o: $(hdrdir)/ruby/internal/intern/vm.h +invalid.o: $(hdrdir)/ruby/internal/interpreter.h +invalid.o: $(hdrdir)/ruby/internal/iterator.h +invalid.o: $(hdrdir)/ruby/internal/memory.h +invalid.o: $(hdrdir)/ruby/internal/method.h +invalid.o: $(hdrdir)/ruby/internal/module.h +invalid.o: $(hdrdir)/ruby/internal/newobj.h +invalid.o: $(hdrdir)/ruby/internal/scan_args.h +invalid.o: $(hdrdir)/ruby/internal/special_consts.h +invalid.o: $(hdrdir)/ruby/internal/static_assert.h +invalid.o: $(hdrdir)/ruby/internal/stdalign.h +invalid.o: $(hdrdir)/ruby/internal/stdbool.h +invalid.o: $(hdrdir)/ruby/internal/stdckdint.h +invalid.o: $(hdrdir)/ruby/internal/symbol.h +invalid.o: $(hdrdir)/ruby/internal/value.h +invalid.o: $(hdrdir)/ruby/internal/value_type.h +invalid.o: $(hdrdir)/ruby/internal/variable.h +invalid.o: $(hdrdir)/ruby/internal/warning_push.h +invalid.o: $(hdrdir)/ruby/internal/xmalloc.h +invalid.o: $(hdrdir)/ruby/missing.h +invalid.o: $(hdrdir)/ruby/ruby.h +invalid.o: $(hdrdir)/ruby/st.h +invalid.o: $(hdrdir)/ruby/subst.h +invalid.o: invalid.c rb_fatal.o: $(RUBY_EXTCONF_H) rb_fatal.o: $(arch_hdrdir)/ruby/config.h rb_fatal.o: $(hdrdir)/ruby.h @@ -128,6 +451,7 @@ rb_fatal.o: $(hdrdir)/ruby/internal/intern/re.h rb_fatal.o: $(hdrdir)/ruby/internal/intern/ruby.h rb_fatal.o: $(hdrdir)/ruby/internal/intern/select.h rb_fatal.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +rb_fatal.o: $(hdrdir)/ruby/internal/intern/set.h rb_fatal.o: $(hdrdir)/ruby/internal/intern/signal.h rb_fatal.o: $(hdrdir)/ruby/internal/intern/sprintf.h rb_fatal.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +471,7 @@ rb_fatal.o: $(hdrdir)/ruby/internal/special_consts.h rb_fatal.o: $(hdrdir)/ruby/internal/static_assert.h rb_fatal.o: $(hdrdir)/ruby/internal/stdalign.h rb_fatal.o: $(hdrdir)/ruby/internal/stdbool.h +rb_fatal.o: $(hdrdir)/ruby/internal/stdckdint.h rb_fatal.o: $(hdrdir)/ruby/internal/symbol.h rb_fatal.o: $(hdrdir)/ruby/internal/value.h rb_fatal.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/fatal/extconf.rb b/ext/-test-/fatal/extconf.rb index d5849c0733..ca51178a18 100644 --- a/ext/-test-/fatal/extconf.rb +++ b/ext/-test-/fatal/extconf.rb @@ -1,2 +1,3 @@ # frozen_string_literal: false -create_makefile("-test-/fatal/rb_fatal") +require_relative "../auto_ext.rb" +auto_ext diff --git a/ext/-test-/fatal/init.c b/ext/-test-/fatal/init.c new file mode 100644 index 0000000000..3b71708789 --- /dev/null +++ b/ext/-test-/fatal/init.c @@ -0,0 +1,10 @@ +#include "ruby.h" + +#define init(n) {void Init_##n(VALUE klass); Init_##n(klass);} + +void +Init_fatal(void) +{ + VALUE klass = rb_define_module("Bug"); + TEST_INIT_FUNCS(init); +} diff --git a/ext/-test-/fatal/invalid.c b/ext/-test-/fatal/invalid.c new file mode 100644 index 0000000000..6fd970b181 --- /dev/null +++ b/ext/-test-/fatal/invalid.c @@ -0,0 +1,22 @@ +#include <ruby.h> + +static VALUE +invalid_call(VALUE obj, VALUE address) +{ + typedef VALUE (*func_type)(VALUE); + + return (*(func_type)NUM2PTR(address))(obj); +} + +static VALUE +invalid_access(VALUE obj, VALUE address) +{ + return *(VALUE *)NUM2PTR(address) == obj ? Qtrue : Qfalse; +} + +void +Init_invalid(VALUE mBug) +{ + rb_define_singleton_method(mBug, "invalid_call", invalid_call, 1); + rb_define_singleton_method(mBug, "invalid_access", invalid_access, 1); +} diff --git a/ext/-test-/fatal/rb_fatal.c b/ext/-test-/fatal/rb_fatal.c index eedbc51f8b..6c7bb89628 100644 --- a/ext/-test-/fatal/rb_fatal.c +++ b/ext/-test-/fatal/rb_fatal.c @@ -13,8 +13,7 @@ ruby_fatal(VALUE obj, VALUE msg) } void -Init_rb_fatal(void) +Init_rb_fatal(VALUE mBug) { - VALUE mBug = rb_define_module("Bug"); rb_define_singleton_method(mBug, "rb_fatal", ruby_fatal, 1); } diff --git a/ext/-test-/file/depend b/ext/-test-/file/depend index 2c8a04e433..fe320f3d44 100644 --- a/ext/-test-/file/depend +++ b/ext/-test-/file/depend @@ -137,6 +137,7 @@ fs.o: $(hdrdir)/ruby/internal/intern/re.h fs.o: $(hdrdir)/ruby/internal/intern/ruby.h fs.o: $(hdrdir)/ruby/internal/intern/select.h fs.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +fs.o: $(hdrdir)/ruby/internal/intern/set.h fs.o: $(hdrdir)/ruby/internal/intern/signal.h fs.o: $(hdrdir)/ruby/internal/intern/sprintf.h fs.o: $(hdrdir)/ruby/internal/intern/string.h @@ -156,6 +157,7 @@ fs.o: $(hdrdir)/ruby/internal/special_consts.h fs.o: $(hdrdir)/ruby/internal/static_assert.h fs.o: $(hdrdir)/ruby/internal/stdalign.h fs.o: $(hdrdir)/ruby/internal/stdbool.h +fs.o: $(hdrdir)/ruby/internal/stdckdint.h fs.o: $(hdrdir)/ruby/internal/symbol.h fs.o: $(hdrdir)/ruby/internal/value.h fs.o: $(hdrdir)/ruby/internal/value_type.h @@ -299,6 +301,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -318,6 +321,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -467,6 +471,7 @@ newline_conv.o: $(hdrdir)/ruby/internal/intern/re.h newline_conv.o: $(hdrdir)/ruby/internal/intern/ruby.h newline_conv.o: $(hdrdir)/ruby/internal/intern/select.h newline_conv.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/set.h newline_conv.o: $(hdrdir)/ruby/internal/intern/signal.h newline_conv.o: $(hdrdir)/ruby/internal/intern/sprintf.h newline_conv.o: $(hdrdir)/ruby/internal/intern/string.h @@ -486,6 +491,7 @@ newline_conv.o: $(hdrdir)/ruby/internal/special_consts.h newline_conv.o: $(hdrdir)/ruby/internal/static_assert.h newline_conv.o: $(hdrdir)/ruby/internal/stdalign.h newline_conv.o: $(hdrdir)/ruby/internal/stdbool.h +newline_conv.o: $(hdrdir)/ruby/internal/stdckdint.h newline_conv.o: $(hdrdir)/ruby/internal/symbol.h newline_conv.o: $(hdrdir)/ruby/internal/value.h newline_conv.o: $(hdrdir)/ruby/internal/value_type.h @@ -638,6 +644,7 @@ stat.o: $(hdrdir)/ruby/internal/intern/re.h stat.o: $(hdrdir)/ruby/internal/intern/ruby.h stat.o: $(hdrdir)/ruby/internal/intern/select.h stat.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +stat.o: $(hdrdir)/ruby/internal/intern/set.h stat.o: $(hdrdir)/ruby/internal/intern/signal.h stat.o: $(hdrdir)/ruby/internal/intern/sprintf.h stat.o: $(hdrdir)/ruby/internal/intern/string.h @@ -657,6 +664,7 @@ stat.o: $(hdrdir)/ruby/internal/special_consts.h stat.o: $(hdrdir)/ruby/internal/static_assert.h stat.o: $(hdrdir)/ruby/internal/stdalign.h stat.o: $(hdrdir)/ruby/internal/stdbool.h +stat.o: $(hdrdir)/ruby/internal/stdckdint.h stat.o: $(hdrdir)/ruby/internal/symbol.h stat.o: $(hdrdir)/ruby/internal/value.h stat.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/float/depend b/ext/-test-/float/depend index 9580a0416c..334ed33c3b 100644 --- a/ext/-test-/float/depend +++ b/ext/-test-/float/depend @@ -131,6 +131,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -150,6 +151,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -290,6 +292,7 @@ nextafter.o: $(hdrdir)/ruby/internal/intern/re.h nextafter.o: $(hdrdir)/ruby/internal/intern/ruby.h nextafter.o: $(hdrdir)/ruby/internal/intern/select.h nextafter.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +nextafter.o: $(hdrdir)/ruby/internal/intern/set.h nextafter.o: $(hdrdir)/ruby/internal/intern/signal.h nextafter.o: $(hdrdir)/ruby/internal/intern/sprintf.h nextafter.o: $(hdrdir)/ruby/internal/intern/string.h @@ -309,6 +312,7 @@ nextafter.o: $(hdrdir)/ruby/internal/special_consts.h nextafter.o: $(hdrdir)/ruby/internal/static_assert.h nextafter.o: $(hdrdir)/ruby/internal/stdalign.h nextafter.o: $(hdrdir)/ruby/internal/stdbool.h +nextafter.o: $(hdrdir)/ruby/internal/stdckdint.h nextafter.o: $(hdrdir)/ruby/internal/symbol.h nextafter.o: $(hdrdir)/ruby/internal/value.h nextafter.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/funcall/depend b/ext/-test-/funcall/depend index 6719e4e676..e54370306f 100644 --- a/ext/-test-/funcall/depend +++ b/ext/-test-/funcall/depend @@ -128,6 +128,7 @@ funcall.o: $(hdrdir)/ruby/internal/intern/re.h funcall.o: $(hdrdir)/ruby/internal/intern/ruby.h funcall.o: $(hdrdir)/ruby/internal/intern/select.h funcall.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +funcall.o: $(hdrdir)/ruby/internal/intern/set.h funcall.o: $(hdrdir)/ruby/internal/intern/signal.h funcall.o: $(hdrdir)/ruby/internal/intern/sprintf.h funcall.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ funcall.o: $(hdrdir)/ruby/internal/special_consts.h funcall.o: $(hdrdir)/ruby/internal/static_assert.h funcall.o: $(hdrdir)/ruby/internal/stdalign.h funcall.o: $(hdrdir)/ruby/internal/stdbool.h +funcall.o: $(hdrdir)/ruby/internal/stdckdint.h funcall.o: $(hdrdir)/ruby/internal/symbol.h funcall.o: $(hdrdir)/ruby/internal/value.h funcall.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/gvl/call_without_gvl/depend b/ext/-test-/gvl/call_without_gvl/depend index a4987a65ca..236d1e1d3b 100644 --- a/ext/-test-/gvl/call_without_gvl/depend +++ b/ext/-test-/gvl/call_without_gvl/depend @@ -127,6 +127,7 @@ call_without_gvl.o: $(hdrdir)/ruby/internal/intern/re.h call_without_gvl.o: $(hdrdir)/ruby/internal/intern/ruby.h call_without_gvl.o: $(hdrdir)/ruby/internal/intern/select.h call_without_gvl.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +call_without_gvl.o: $(hdrdir)/ruby/internal/intern/set.h call_without_gvl.o: $(hdrdir)/ruby/internal/intern/signal.h call_without_gvl.o: $(hdrdir)/ruby/internal/intern/sprintf.h call_without_gvl.o: $(hdrdir)/ruby/internal/intern/string.h @@ -146,6 +147,7 @@ call_without_gvl.o: $(hdrdir)/ruby/internal/special_consts.h call_without_gvl.o: $(hdrdir)/ruby/internal/static_assert.h call_without_gvl.o: $(hdrdir)/ruby/internal/stdalign.h call_without_gvl.o: $(hdrdir)/ruby/internal/stdbool.h +call_without_gvl.o: $(hdrdir)/ruby/internal/stdckdint.h call_without_gvl.o: $(hdrdir)/ruby/internal/symbol.h call_without_gvl.o: $(hdrdir)/ruby/internal/value.h call_without_gvl.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/hash/depend b/ext/-test-/hash/depend index 58d9a6247e..416b93f9de 100644 --- a/ext/-test-/hash/depend +++ b/ext/-test-/hash/depend @@ -128,6 +128,7 @@ delete.o: $(hdrdir)/ruby/internal/intern/re.h delete.o: $(hdrdir)/ruby/internal/intern/ruby.h delete.o: $(hdrdir)/ruby/internal/intern/select.h delete.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +delete.o: $(hdrdir)/ruby/internal/intern/set.h delete.o: $(hdrdir)/ruby/internal/intern/signal.h delete.o: $(hdrdir)/ruby/internal/intern/sprintf.h delete.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ delete.o: $(hdrdir)/ruby/internal/special_consts.h delete.o: $(hdrdir)/ruby/internal/static_assert.h delete.o: $(hdrdir)/ruby/internal/stdalign.h delete.o: $(hdrdir)/ruby/internal/stdbool.h +delete.o: $(hdrdir)/ruby/internal/stdckdint.h delete.o: $(hdrdir)/ruby/internal/symbol.h delete.o: $(hdrdir)/ruby/internal/value.h delete.o: $(hdrdir)/ruby/internal/value_type.h @@ -287,6 +289,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -306,6 +309,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/integer/depend b/ext/-test-/integer/depend index b68a68ce73..0ea007e814 100644 --- a/ext/-test-/integer/depend +++ b/ext/-test-/integer/depend @@ -128,6 +128,7 @@ core_ext.o: $(hdrdir)/ruby/internal/intern/re.h core_ext.o: $(hdrdir)/ruby/internal/intern/ruby.h core_ext.o: $(hdrdir)/ruby/internal/intern/select.h core_ext.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +core_ext.o: $(hdrdir)/ruby/internal/intern/set.h core_ext.o: $(hdrdir)/ruby/internal/intern/signal.h core_ext.o: $(hdrdir)/ruby/internal/intern/sprintf.h core_ext.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ core_ext.o: $(hdrdir)/ruby/internal/special_consts.h core_ext.o: $(hdrdir)/ruby/internal/static_assert.h core_ext.o: $(hdrdir)/ruby/internal/stdalign.h core_ext.o: $(hdrdir)/ruby/internal/stdbool.h +core_ext.o: $(hdrdir)/ruby/internal/stdckdint.h core_ext.o: $(hdrdir)/ruby/internal/symbol.h core_ext.o: $(hdrdir)/ruby/internal/value.h core_ext.o: $(hdrdir)/ruby/internal/value_type.h @@ -295,6 +297,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -314,6 +317,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -454,6 +458,7 @@ my_integer.o: $(hdrdir)/ruby/internal/intern/re.h my_integer.o: $(hdrdir)/ruby/internal/intern/ruby.h my_integer.o: $(hdrdir)/ruby/internal/intern/select.h my_integer.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +my_integer.o: $(hdrdir)/ruby/internal/intern/set.h my_integer.o: $(hdrdir)/ruby/internal/intern/signal.h my_integer.o: $(hdrdir)/ruby/internal/intern/sprintf.h my_integer.o: $(hdrdir)/ruby/internal/intern/string.h @@ -473,6 +478,7 @@ my_integer.o: $(hdrdir)/ruby/internal/special_consts.h my_integer.o: $(hdrdir)/ruby/internal/static_assert.h my_integer.o: $(hdrdir)/ruby/internal/stdalign.h my_integer.o: $(hdrdir)/ruby/internal/stdbool.h +my_integer.o: $(hdrdir)/ruby/internal/stdckdint.h my_integer.o: $(hdrdir)/ruby/internal/symbol.h my_integer.o: $(hdrdir)/ruby/internal/value.h my_integer.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/integer/my_integer.c b/ext/-test-/integer/my_integer.c index d86474bd7d..94f14d2765 100644 --- a/ext/-test-/integer/my_integer.c +++ b/ext/-test-/integer/my_integer.c @@ -1,9 +1,13 @@ #include "ruby.h" +static const rb_data_type_t my_integer_type = { + "MyInteger", {0}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY +}; + static VALUE my_integer_s_new(VALUE klass) { - return Data_Wrap_Struct(klass, 0, 0, 0); + return TypedData_Wrap_Struct(klass, &my_integer_type, 0); } void diff --git a/ext/-test-/iseq_load/depend b/ext/-test-/iseq_load/depend index 30deb6e039..9361ddb938 100644 --- a/ext/-test-/iseq_load/depend +++ b/ext/-test-/iseq_load/depend @@ -128,6 +128,7 @@ iseq_load.o: $(hdrdir)/ruby/internal/intern/re.h iseq_load.o: $(hdrdir)/ruby/internal/intern/ruby.h iseq_load.o: $(hdrdir)/ruby/internal/intern/select.h iseq_load.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +iseq_load.o: $(hdrdir)/ruby/internal/intern/set.h iseq_load.o: $(hdrdir)/ruby/internal/intern/signal.h iseq_load.o: $(hdrdir)/ruby/internal/intern/sprintf.h iseq_load.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ iseq_load.o: $(hdrdir)/ruby/internal/special_consts.h iseq_load.o: $(hdrdir)/ruby/internal/static_assert.h iseq_load.o: $(hdrdir)/ruby/internal/stdalign.h iseq_load.o: $(hdrdir)/ruby/internal/stdbool.h +iseq_load.o: $(hdrdir)/ruby/internal/stdckdint.h iseq_load.o: $(hdrdir)/ruby/internal/symbol.h iseq_load.o: $(hdrdir)/ruby/internal/value.h iseq_load.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/iter/depend b/ext/-test-/iter/depend index 077a283532..161947382c 100644 --- a/ext/-test-/iter/depend +++ b/ext/-test-/iter/depend @@ -128,6 +128,7 @@ break.o: $(hdrdir)/ruby/internal/intern/re.h break.o: $(hdrdir)/ruby/internal/intern/ruby.h break.o: $(hdrdir)/ruby/internal/intern/select.h break.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +break.o: $(hdrdir)/ruby/internal/intern/set.h break.o: $(hdrdir)/ruby/internal/intern/signal.h break.o: $(hdrdir)/ruby/internal/intern/sprintf.h break.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ break.o: $(hdrdir)/ruby/internal/special_consts.h break.o: $(hdrdir)/ruby/internal/static_assert.h break.o: $(hdrdir)/ruby/internal/stdalign.h break.o: $(hdrdir)/ruby/internal/stdbool.h +break.o: $(hdrdir)/ruby/internal/stdckdint.h break.o: $(hdrdir)/ruby/internal/symbol.h break.o: $(hdrdir)/ruby/internal/value.h break.o: $(hdrdir)/ruby/internal/value_type.h @@ -287,6 +289,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -306,6 +309,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -446,6 +450,7 @@ yield.o: $(hdrdir)/ruby/internal/intern/re.h yield.o: $(hdrdir)/ruby/internal/intern/ruby.h yield.o: $(hdrdir)/ruby/internal/intern/select.h yield.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +yield.o: $(hdrdir)/ruby/internal/intern/set.h yield.o: $(hdrdir)/ruby/internal/intern/signal.h yield.o: $(hdrdir)/ruby/internal/intern/sprintf.h yield.o: $(hdrdir)/ruby/internal/intern/string.h @@ -465,6 +470,7 @@ yield.o: $(hdrdir)/ruby/internal/special_consts.h yield.o: $(hdrdir)/ruby/internal/static_assert.h yield.o: $(hdrdir)/ruby/internal/stdalign.h yield.o: $(hdrdir)/ruby/internal/stdbool.h +yield.o: $(hdrdir)/ruby/internal/stdckdint.h yield.o: $(hdrdir)/ruby/internal/symbol.h yield.o: $(hdrdir)/ruby/internal/value.h yield.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/load/dot.dot/depend b/ext/-test-/load/dot.dot/depend index be835d3ea5..339837d183 100644 --- a/ext/-test-/load/dot.dot/depend +++ b/ext/-test-/load/dot.dot/depend @@ -128,6 +128,7 @@ dot.dot.o: $(hdrdir)/ruby/internal/intern/re.h dot.dot.o: $(hdrdir)/ruby/internal/intern/ruby.h dot.dot.o: $(hdrdir)/ruby/internal/intern/select.h dot.dot.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +dot.dot.o: $(hdrdir)/ruby/internal/intern/set.h dot.dot.o: $(hdrdir)/ruby/internal/intern/signal.h dot.dot.o: $(hdrdir)/ruby/internal/intern/sprintf.h dot.dot.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ dot.dot.o: $(hdrdir)/ruby/internal/special_consts.h dot.dot.o: $(hdrdir)/ruby/internal/static_assert.h dot.dot.o: $(hdrdir)/ruby/internal/stdalign.h dot.dot.o: $(hdrdir)/ruby/internal/stdbool.h +dot.dot.o: $(hdrdir)/ruby/internal/stdckdint.h dot.dot.o: $(hdrdir)/ruby/internal/symbol.h dot.dot.o: $(hdrdir)/ruby/internal/value.h dot.dot.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/load/protect/depend b/ext/-test-/load/protect/depend index 57cf029901..c76c6f88ed 100644 --- a/ext/-test-/load/protect/depend +++ b/ext/-test-/load/protect/depend @@ -128,6 +128,7 @@ protect.o: $(hdrdir)/ruby/internal/intern/re.h protect.o: $(hdrdir)/ruby/internal/intern/ruby.h protect.o: $(hdrdir)/ruby/internal/intern/select.h protect.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +protect.o: $(hdrdir)/ruby/internal/intern/set.h protect.o: $(hdrdir)/ruby/internal/intern/signal.h protect.o: $(hdrdir)/ruby/internal/intern/sprintf.h protect.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ protect.o: $(hdrdir)/ruby/internal/special_consts.h protect.o: $(hdrdir)/ruby/internal/static_assert.h protect.o: $(hdrdir)/ruby/internal/stdalign.h protect.o: $(hdrdir)/ruby/internal/stdbool.h +protect.o: $(hdrdir)/ruby/internal/stdckdint.h protect.o: $(hdrdir)/ruby/internal/symbol.h protect.o: $(hdrdir)/ruby/internal/value.h protect.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/load/resolve_symbol_resolver/depend b/ext/-test-/load/resolve_symbol_resolver/depend new file mode 100644 index 0000000000..f422898b69 --- /dev/null +++ b/ext/-test-/load/resolve_symbol_resolver/depend @@ -0,0 +1,163 @@ +# AUTOGENERATED DEPENDENCIES START +resolve_symbol_resolver.o: $(RUBY_EXTCONF_H) +resolve_symbol_resolver.o: $(arch_hdrdir)/ruby/config.h +resolve_symbol_resolver.o: $(hdrdir)/ruby.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/assert.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/backward.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/backward/2/assume.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/backward/2/attributes.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/backward/2/bool.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/backward/2/inttypes.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/backward/2/limits.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/backward/2/long_long.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/backward/2/stdalign.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/backward/2/stdarg.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/defines.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/intern.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/abi.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/anyargs.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/arithmetic.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/arithmetic/char.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/arithmetic/double.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/arithmetic/int.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/arithmetic/long.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/arithmetic/short.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/assume.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/artificial.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/cold.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/const.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/constexpr.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/deprecated.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/error.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/forceinline.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/format.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/noalias.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/noexcept.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/noinline.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/nonnull.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/noreturn.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/pure.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/restrict.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/warning.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/attr/weakref.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/cast.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/compiler_is.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/compiler_since.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/config.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/constant_p.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/core.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/core/rarray.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/core/rbasic.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/core/rbignum.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/core/rclass.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/core/rdata.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/core/rfile.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/core/rhash.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/core/robject.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/core/rregexp.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/core/rstring.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/core/rstruct.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/ctype.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/dllexport.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/dosish.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/error.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/eval.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/event.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/fl_type.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/gc.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/glob.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/globals.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/has/attribute.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/has/builtin.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/has/c_attribute.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/has/extension.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/has/feature.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/has/warning.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/array.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/bignum.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/class.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/compar.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/complex.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/cont.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/dir.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/enum.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/enumerator.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/error.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/eval.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/file.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/hash.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/io.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/load.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/marshal.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/numeric.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/object.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/parse.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/proc.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/process.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/random.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/range.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/rational.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/re.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/ruby.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/select.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/set.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/signal.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/sprintf.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/string.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/struct.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/thread.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/time.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/variable.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/intern/vm.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/interpreter.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/iterator.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/memory.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/method.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/module.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/newobj.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/scan_args.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/special_consts.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/static_assert.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/stdalign.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/stdbool.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/stdckdint.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/symbol.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/value.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/value_type.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/variable.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/warning_push.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/xmalloc.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/missing.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/ruby.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/st.h +resolve_symbol_resolver.o: $(hdrdir)/ruby/subst.h +resolve_symbol_resolver.o: resolve_symbol_resolver.c +# AUTOGENERATED DEPENDENCIES END diff --git a/ext/-test-/load/resolve_symbol_resolver/resolve_symbol_resolver.c b/ext/-test-/load/resolve_symbol_resolver/resolve_symbol_resolver.c index a856319cfb..6cc07cc1f8 100644 --- a/ext/-test-/load/resolve_symbol_resolver/resolve_symbol_resolver.c +++ b/ext/-test-/load/resolve_symbol_resolver/resolve_symbol_resolver.c @@ -5,6 +5,7 @@ typedef VALUE(*target_func)(VALUE); static target_func rst_any_method; +#define resolve_func(file, name) (target_func)(uintptr_t)rb_ext_resolve_symbol(file, name) VALUE rsr_any_method(VALUE klass) { @@ -15,7 +16,7 @@ VALUE rsr_try_resolve_fname(VALUE klass) { target_func rst_something_missing = - (target_func) rb_ext_resolve_symbol("-test-/load/resolve_symbol_missing", "rst_any_method"); + resolve_func("-test-/load/resolve_symbol_missing", "rst_any_method"); if (rst_something_missing == NULL) { // This should be done in Init_*, so the error is LoadError rb_raise(rb_eLoadError, "symbol not found: missing fname"); @@ -27,7 +28,7 @@ VALUE rsr_try_resolve_sname(VALUE klass) { target_func rst_something_missing = - (target_func)rb_ext_resolve_symbol("-test-/load/resolve_symbol_target", "rst_something_missing"); + resolve_func("-test-/load/resolve_symbol_target", "rst_something_missing"); if (rst_something_missing == NULL) { // This should be done in Init_*, so the error is LoadError rb_raise(rb_eLoadError, "symbol not found: missing sname"); @@ -43,7 +44,7 @@ Init_resolve_symbol_resolver(void) * If the module and methods are defined before raising LoadError, retrying `require "this.so"` will * cause re-defining those methods (and will be warned). */ - rst_any_method = (target_func)rb_ext_resolve_symbol("-test-/load/resolve_symbol_target", "rst_any_method"); + rst_any_method = resolve_func("-test-/load/resolve_symbol_target", "rst_any_method"); if (rst_any_method == NULL) { rb_raise(rb_eLoadError, "resolve_symbol_target is not loaded"); } diff --git a/ext/-test-/load/resolve_symbol_target/depend b/ext/-test-/load/resolve_symbol_target/depend new file mode 100644 index 0000000000..aa0b5327be --- /dev/null +++ b/ext/-test-/load/resolve_symbol_target/depend @@ -0,0 +1,164 @@ +# AUTOGENERATED DEPENDENCIES START +resolve_symbol_target.o: $(RUBY_EXTCONF_H) +resolve_symbol_target.o: $(arch_hdrdir)/ruby/config.h +resolve_symbol_target.o: $(hdrdir)/ruby.h +resolve_symbol_target.o: $(hdrdir)/ruby/assert.h +resolve_symbol_target.o: $(hdrdir)/ruby/backward.h +resolve_symbol_target.o: $(hdrdir)/ruby/backward/2/assume.h +resolve_symbol_target.o: $(hdrdir)/ruby/backward/2/attributes.h +resolve_symbol_target.o: $(hdrdir)/ruby/backward/2/bool.h +resolve_symbol_target.o: $(hdrdir)/ruby/backward/2/inttypes.h +resolve_symbol_target.o: $(hdrdir)/ruby/backward/2/limits.h +resolve_symbol_target.o: $(hdrdir)/ruby/backward/2/long_long.h +resolve_symbol_target.o: $(hdrdir)/ruby/backward/2/stdalign.h +resolve_symbol_target.o: $(hdrdir)/ruby/backward/2/stdarg.h +resolve_symbol_target.o: $(hdrdir)/ruby/defines.h +resolve_symbol_target.o: $(hdrdir)/ruby/intern.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/abi.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/anyargs.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/arithmetic.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/arithmetic/char.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/arithmetic/double.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/arithmetic/int.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/arithmetic/long.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/arithmetic/short.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/assume.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/artificial.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/cold.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/const.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/constexpr.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/deprecated.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/error.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/forceinline.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/format.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/noalias.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/noexcept.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/noinline.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/nonnull.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/noreturn.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/pure.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/restrict.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/warning.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/attr/weakref.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/cast.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/compiler_is.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/compiler_since.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/config.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/constant_p.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/core.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/core/rarray.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/core/rbasic.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/core/rbignum.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/core/rclass.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/core/rdata.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/core/rfile.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/core/rhash.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/core/robject.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/core/rregexp.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/core/rstring.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/core/rstruct.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/ctype.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/dllexport.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/dosish.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/error.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/eval.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/event.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/fl_type.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/gc.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/glob.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/globals.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/has/attribute.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/has/builtin.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/has/c_attribute.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/has/extension.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/has/feature.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/has/warning.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/array.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/bignum.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/class.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/compar.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/complex.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/cont.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/dir.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/enum.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/enumerator.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/error.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/eval.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/file.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/hash.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/io.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/load.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/marshal.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/numeric.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/object.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/parse.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/proc.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/process.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/random.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/range.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/rational.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/re.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/ruby.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/select.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/set.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/signal.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/sprintf.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/string.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/struct.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/thread.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/time.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/variable.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/intern/vm.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/interpreter.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/iterator.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/memory.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/method.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/module.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/newobj.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/scan_args.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/special_consts.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/static_assert.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/stdalign.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/stdbool.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/stdckdint.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/symbol.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/value.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/value_type.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/variable.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/warning_push.h +resolve_symbol_target.o: $(hdrdir)/ruby/internal/xmalloc.h +resolve_symbol_target.o: $(hdrdir)/ruby/missing.h +resolve_symbol_target.o: $(hdrdir)/ruby/ruby.h +resolve_symbol_target.o: $(hdrdir)/ruby/st.h +resolve_symbol_target.o: $(hdrdir)/ruby/subst.h +resolve_symbol_target.o: resolve_symbol_target.c +resolve_symbol_target.o: resolve_symbol_target.h +# AUTOGENERATED DEPENDENCIES END diff --git a/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.def b/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.def deleted file mode 100644 index c2ed3610fe..0000000000 --- a/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.def +++ /dev/null @@ -1,4 +0,0 @@ -LIBRARY resolve_symbol_target -EXPORTS - Init_resolve_symbol_target - rst_any_method diff --git a/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.h b/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.h index 7d471bf360..847dcb7dd3 100644 --- a/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.h +++ b/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.h @@ -1,4 +1,4 @@ #include <ruby.h> #include "ruby/internal/dllexport.h" -RUBY_EXTERN VALUE rst_any_method(VALUE); +RUBY_FUNC_EXPORTED VALUE rst_any_method(VALUE); diff --git a/ext/-test-/load/stringify_symbols/depend b/ext/-test-/load/stringify_symbols/depend new file mode 100644 index 0000000000..2d4d79a7b7 --- /dev/null +++ b/ext/-test-/load/stringify_symbols/depend @@ -0,0 +1,164 @@ +# AUTOGENERATED DEPENDENCIES START +stringify_symbols.o: $(RUBY_EXTCONF_H) +stringify_symbols.o: $(arch_hdrdir)/ruby/config.h +stringify_symbols.o: $(hdrdir)/ruby.h +stringify_symbols.o: $(hdrdir)/ruby/assert.h +stringify_symbols.o: $(hdrdir)/ruby/backward.h +stringify_symbols.o: $(hdrdir)/ruby/backward/2/assume.h +stringify_symbols.o: $(hdrdir)/ruby/backward/2/attributes.h +stringify_symbols.o: $(hdrdir)/ruby/backward/2/bool.h +stringify_symbols.o: $(hdrdir)/ruby/backward/2/inttypes.h +stringify_symbols.o: $(hdrdir)/ruby/backward/2/limits.h +stringify_symbols.o: $(hdrdir)/ruby/backward/2/long_long.h +stringify_symbols.o: $(hdrdir)/ruby/backward/2/stdalign.h +stringify_symbols.o: $(hdrdir)/ruby/backward/2/stdarg.h +stringify_symbols.o: $(hdrdir)/ruby/defines.h +stringify_symbols.o: $(hdrdir)/ruby/intern.h +stringify_symbols.o: $(hdrdir)/ruby/internal/abi.h +stringify_symbols.o: $(hdrdir)/ruby/internal/anyargs.h +stringify_symbols.o: $(hdrdir)/ruby/internal/arithmetic.h +stringify_symbols.o: $(hdrdir)/ruby/internal/arithmetic/char.h +stringify_symbols.o: $(hdrdir)/ruby/internal/arithmetic/double.h +stringify_symbols.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +stringify_symbols.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +stringify_symbols.o: $(hdrdir)/ruby/internal/arithmetic/int.h +stringify_symbols.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +stringify_symbols.o: $(hdrdir)/ruby/internal/arithmetic/long.h +stringify_symbols.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +stringify_symbols.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +stringify_symbols.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +stringify_symbols.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +stringify_symbols.o: $(hdrdir)/ruby/internal/arithmetic/short.h +stringify_symbols.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +stringify_symbols.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +stringify_symbols.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +stringify_symbols.o: $(hdrdir)/ruby/internal/assume.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/artificial.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/cold.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/const.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/constexpr.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/deprecated.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/error.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/forceinline.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/format.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/noalias.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/noexcept.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/noinline.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/nonnull.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/noreturn.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/pure.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/restrict.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/warning.h +stringify_symbols.o: $(hdrdir)/ruby/internal/attr/weakref.h +stringify_symbols.o: $(hdrdir)/ruby/internal/cast.h +stringify_symbols.o: $(hdrdir)/ruby/internal/compiler_is.h +stringify_symbols.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +stringify_symbols.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +stringify_symbols.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +stringify_symbols.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +stringify_symbols.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +stringify_symbols.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +stringify_symbols.o: $(hdrdir)/ruby/internal/compiler_since.h +stringify_symbols.o: $(hdrdir)/ruby/internal/config.h +stringify_symbols.o: $(hdrdir)/ruby/internal/constant_p.h +stringify_symbols.o: $(hdrdir)/ruby/internal/core.h +stringify_symbols.o: $(hdrdir)/ruby/internal/core/rarray.h +stringify_symbols.o: $(hdrdir)/ruby/internal/core/rbasic.h +stringify_symbols.o: $(hdrdir)/ruby/internal/core/rbignum.h +stringify_symbols.o: $(hdrdir)/ruby/internal/core/rclass.h +stringify_symbols.o: $(hdrdir)/ruby/internal/core/rdata.h +stringify_symbols.o: $(hdrdir)/ruby/internal/core/rfile.h +stringify_symbols.o: $(hdrdir)/ruby/internal/core/rhash.h +stringify_symbols.o: $(hdrdir)/ruby/internal/core/robject.h +stringify_symbols.o: $(hdrdir)/ruby/internal/core/rregexp.h +stringify_symbols.o: $(hdrdir)/ruby/internal/core/rstring.h +stringify_symbols.o: $(hdrdir)/ruby/internal/core/rstruct.h +stringify_symbols.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +stringify_symbols.o: $(hdrdir)/ruby/internal/ctype.h +stringify_symbols.o: $(hdrdir)/ruby/internal/dllexport.h +stringify_symbols.o: $(hdrdir)/ruby/internal/dosish.h +stringify_symbols.o: $(hdrdir)/ruby/internal/error.h +stringify_symbols.o: $(hdrdir)/ruby/internal/eval.h +stringify_symbols.o: $(hdrdir)/ruby/internal/event.h +stringify_symbols.o: $(hdrdir)/ruby/internal/fl_type.h +stringify_symbols.o: $(hdrdir)/ruby/internal/gc.h +stringify_symbols.o: $(hdrdir)/ruby/internal/glob.h +stringify_symbols.o: $(hdrdir)/ruby/internal/globals.h +stringify_symbols.o: $(hdrdir)/ruby/internal/has/attribute.h +stringify_symbols.o: $(hdrdir)/ruby/internal/has/builtin.h +stringify_symbols.o: $(hdrdir)/ruby/internal/has/c_attribute.h +stringify_symbols.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +stringify_symbols.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +stringify_symbols.o: $(hdrdir)/ruby/internal/has/extension.h +stringify_symbols.o: $(hdrdir)/ruby/internal/has/feature.h +stringify_symbols.o: $(hdrdir)/ruby/internal/has/warning.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/array.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/bignum.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/class.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/compar.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/complex.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/cont.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/dir.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/enum.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/enumerator.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/error.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/eval.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/file.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/hash.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/io.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/load.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/marshal.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/numeric.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/object.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/parse.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/proc.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/process.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/random.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/range.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/rational.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/re.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/ruby.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/select.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/set.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/signal.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/sprintf.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/string.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/struct.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/thread.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/time.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/variable.h +stringify_symbols.o: $(hdrdir)/ruby/internal/intern/vm.h +stringify_symbols.o: $(hdrdir)/ruby/internal/interpreter.h +stringify_symbols.o: $(hdrdir)/ruby/internal/iterator.h +stringify_symbols.o: $(hdrdir)/ruby/internal/memory.h +stringify_symbols.o: $(hdrdir)/ruby/internal/method.h +stringify_symbols.o: $(hdrdir)/ruby/internal/module.h +stringify_symbols.o: $(hdrdir)/ruby/internal/newobj.h +stringify_symbols.o: $(hdrdir)/ruby/internal/scan_args.h +stringify_symbols.o: $(hdrdir)/ruby/internal/special_consts.h +stringify_symbols.o: $(hdrdir)/ruby/internal/static_assert.h +stringify_symbols.o: $(hdrdir)/ruby/internal/stdalign.h +stringify_symbols.o: $(hdrdir)/ruby/internal/stdbool.h +stringify_symbols.o: $(hdrdir)/ruby/internal/stdckdint.h +stringify_symbols.o: $(hdrdir)/ruby/internal/symbol.h +stringify_symbols.o: $(hdrdir)/ruby/internal/value.h +stringify_symbols.o: $(hdrdir)/ruby/internal/value_type.h +stringify_symbols.o: $(hdrdir)/ruby/internal/variable.h +stringify_symbols.o: $(hdrdir)/ruby/internal/warning_push.h +stringify_symbols.o: $(hdrdir)/ruby/internal/xmalloc.h +stringify_symbols.o: $(hdrdir)/ruby/missing.h +stringify_symbols.o: $(hdrdir)/ruby/ruby.h +stringify_symbols.o: $(hdrdir)/ruby/st.h +stringify_symbols.o: $(hdrdir)/ruby/subst.h +stringify_symbols.o: $(hdrdir)/ruby/util.h +stringify_symbols.o: stringify_symbols.c +# AUTOGENERATED DEPENDENCIES END diff --git a/ext/-test-/load/stringify_target/depend b/ext/-test-/load/stringify_target/depend new file mode 100644 index 0000000000..c66575d4e4 --- /dev/null +++ b/ext/-test-/load/stringify_target/depend @@ -0,0 +1,164 @@ +# AUTOGENERATED DEPENDENCIES START +stringify_target.o: $(RUBY_EXTCONF_H) +stringify_target.o: $(arch_hdrdir)/ruby/config.h +stringify_target.o: $(hdrdir)/ruby.h +stringify_target.o: $(hdrdir)/ruby/assert.h +stringify_target.o: $(hdrdir)/ruby/backward.h +stringify_target.o: $(hdrdir)/ruby/backward/2/assume.h +stringify_target.o: $(hdrdir)/ruby/backward/2/attributes.h +stringify_target.o: $(hdrdir)/ruby/backward/2/bool.h +stringify_target.o: $(hdrdir)/ruby/backward/2/inttypes.h +stringify_target.o: $(hdrdir)/ruby/backward/2/limits.h +stringify_target.o: $(hdrdir)/ruby/backward/2/long_long.h +stringify_target.o: $(hdrdir)/ruby/backward/2/stdalign.h +stringify_target.o: $(hdrdir)/ruby/backward/2/stdarg.h +stringify_target.o: $(hdrdir)/ruby/defines.h +stringify_target.o: $(hdrdir)/ruby/intern.h +stringify_target.o: $(hdrdir)/ruby/internal/abi.h +stringify_target.o: $(hdrdir)/ruby/internal/anyargs.h +stringify_target.o: $(hdrdir)/ruby/internal/arithmetic.h +stringify_target.o: $(hdrdir)/ruby/internal/arithmetic/char.h +stringify_target.o: $(hdrdir)/ruby/internal/arithmetic/double.h +stringify_target.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +stringify_target.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +stringify_target.o: $(hdrdir)/ruby/internal/arithmetic/int.h +stringify_target.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +stringify_target.o: $(hdrdir)/ruby/internal/arithmetic/long.h +stringify_target.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +stringify_target.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +stringify_target.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +stringify_target.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +stringify_target.o: $(hdrdir)/ruby/internal/arithmetic/short.h +stringify_target.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +stringify_target.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +stringify_target.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +stringify_target.o: $(hdrdir)/ruby/internal/assume.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/artificial.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/cold.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/const.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/constexpr.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/deprecated.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/error.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/forceinline.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/format.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/noalias.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/noexcept.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/noinline.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/nonnull.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/noreturn.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/pure.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/restrict.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/warning.h +stringify_target.o: $(hdrdir)/ruby/internal/attr/weakref.h +stringify_target.o: $(hdrdir)/ruby/internal/cast.h +stringify_target.o: $(hdrdir)/ruby/internal/compiler_is.h +stringify_target.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +stringify_target.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +stringify_target.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +stringify_target.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +stringify_target.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +stringify_target.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +stringify_target.o: $(hdrdir)/ruby/internal/compiler_since.h +stringify_target.o: $(hdrdir)/ruby/internal/config.h +stringify_target.o: $(hdrdir)/ruby/internal/constant_p.h +stringify_target.o: $(hdrdir)/ruby/internal/core.h +stringify_target.o: $(hdrdir)/ruby/internal/core/rarray.h +stringify_target.o: $(hdrdir)/ruby/internal/core/rbasic.h +stringify_target.o: $(hdrdir)/ruby/internal/core/rbignum.h +stringify_target.o: $(hdrdir)/ruby/internal/core/rclass.h +stringify_target.o: $(hdrdir)/ruby/internal/core/rdata.h +stringify_target.o: $(hdrdir)/ruby/internal/core/rfile.h +stringify_target.o: $(hdrdir)/ruby/internal/core/rhash.h +stringify_target.o: $(hdrdir)/ruby/internal/core/robject.h +stringify_target.o: $(hdrdir)/ruby/internal/core/rregexp.h +stringify_target.o: $(hdrdir)/ruby/internal/core/rstring.h +stringify_target.o: $(hdrdir)/ruby/internal/core/rstruct.h +stringify_target.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +stringify_target.o: $(hdrdir)/ruby/internal/ctype.h +stringify_target.o: $(hdrdir)/ruby/internal/dllexport.h +stringify_target.o: $(hdrdir)/ruby/internal/dosish.h +stringify_target.o: $(hdrdir)/ruby/internal/error.h +stringify_target.o: $(hdrdir)/ruby/internal/eval.h +stringify_target.o: $(hdrdir)/ruby/internal/event.h +stringify_target.o: $(hdrdir)/ruby/internal/fl_type.h +stringify_target.o: $(hdrdir)/ruby/internal/gc.h +stringify_target.o: $(hdrdir)/ruby/internal/glob.h +stringify_target.o: $(hdrdir)/ruby/internal/globals.h +stringify_target.o: $(hdrdir)/ruby/internal/has/attribute.h +stringify_target.o: $(hdrdir)/ruby/internal/has/builtin.h +stringify_target.o: $(hdrdir)/ruby/internal/has/c_attribute.h +stringify_target.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +stringify_target.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +stringify_target.o: $(hdrdir)/ruby/internal/has/extension.h +stringify_target.o: $(hdrdir)/ruby/internal/has/feature.h +stringify_target.o: $(hdrdir)/ruby/internal/has/warning.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/array.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/bignum.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/class.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/compar.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/complex.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/cont.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/dir.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/enum.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/enumerator.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/error.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/eval.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/file.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/hash.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/io.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/load.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/marshal.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/numeric.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/object.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/parse.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/proc.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/process.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/random.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/range.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/rational.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/re.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/ruby.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/select.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/set.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/signal.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/sprintf.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/string.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/struct.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/thread.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/time.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/variable.h +stringify_target.o: $(hdrdir)/ruby/internal/intern/vm.h +stringify_target.o: $(hdrdir)/ruby/internal/interpreter.h +stringify_target.o: $(hdrdir)/ruby/internal/iterator.h +stringify_target.o: $(hdrdir)/ruby/internal/memory.h +stringify_target.o: $(hdrdir)/ruby/internal/method.h +stringify_target.o: $(hdrdir)/ruby/internal/module.h +stringify_target.o: $(hdrdir)/ruby/internal/newobj.h +stringify_target.o: $(hdrdir)/ruby/internal/scan_args.h +stringify_target.o: $(hdrdir)/ruby/internal/special_consts.h +stringify_target.o: $(hdrdir)/ruby/internal/static_assert.h +stringify_target.o: $(hdrdir)/ruby/internal/stdalign.h +stringify_target.o: $(hdrdir)/ruby/internal/stdbool.h +stringify_target.o: $(hdrdir)/ruby/internal/stdckdint.h +stringify_target.o: $(hdrdir)/ruby/internal/symbol.h +stringify_target.o: $(hdrdir)/ruby/internal/value.h +stringify_target.o: $(hdrdir)/ruby/internal/value_type.h +stringify_target.o: $(hdrdir)/ruby/internal/variable.h +stringify_target.o: $(hdrdir)/ruby/internal/warning_push.h +stringify_target.o: $(hdrdir)/ruby/internal/xmalloc.h +stringify_target.o: $(hdrdir)/ruby/missing.h +stringify_target.o: $(hdrdir)/ruby/ruby.h +stringify_target.o: $(hdrdir)/ruby/st.h +stringify_target.o: $(hdrdir)/ruby/subst.h +stringify_target.o: stringify_target.c +stringify_target.o: stringify_target.h +# AUTOGENERATED DEPENDENCIES END diff --git a/ext/-test-/load/stringify_target/stringify_target.def b/ext/-test-/load/stringify_target/stringify_target.def deleted file mode 100644 index 89c2b762de..0000000000 --- a/ext/-test-/load/stringify_target/stringify_target.def +++ /dev/null @@ -1,4 +0,0 @@ -LIBRARY stringify_target -EXPORTS - Init_stringify_target - stt_any_method diff --git a/ext/-test-/load/stringify_target/stringify_target.h b/ext/-test-/load/stringify_target/stringify_target.h index 5081f8cbd6..d95fb65d7c 100644 --- a/ext/-test-/load/stringify_target/stringify_target.h +++ b/ext/-test-/load/stringify_target/stringify_target.h @@ -1,4 +1,4 @@ #include <ruby.h> #include "ruby/internal/dllexport.h" -RUBY_EXTERN VALUE stt_any_method(VALUE); +RUBY_FUNC_EXPORTED VALUE stt_any_method(VALUE); diff --git a/ext/-test-/marshal/compat/depend b/ext/-test-/marshal/compat/depend index ff675ccabb..36b9235c23 100644 --- a/ext/-test-/marshal/compat/depend +++ b/ext/-test-/marshal/compat/depend @@ -128,6 +128,7 @@ usrcompat.o: $(hdrdir)/ruby/internal/intern/re.h usrcompat.o: $(hdrdir)/ruby/internal/intern/ruby.h usrcompat.o: $(hdrdir)/ruby/internal/intern/select.h usrcompat.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +usrcompat.o: $(hdrdir)/ruby/internal/intern/set.h usrcompat.o: $(hdrdir)/ruby/internal/intern/signal.h usrcompat.o: $(hdrdir)/ruby/internal/intern/sprintf.h usrcompat.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ usrcompat.o: $(hdrdir)/ruby/internal/special_consts.h usrcompat.o: $(hdrdir)/ruby/internal/static_assert.h usrcompat.o: $(hdrdir)/ruby/internal/stdalign.h usrcompat.o: $(hdrdir)/ruby/internal/stdbool.h +usrcompat.o: $(hdrdir)/ruby/internal/stdckdint.h usrcompat.o: $(hdrdir)/ruby/internal/symbol.h usrcompat.o: $(hdrdir)/ruby/internal/value.h usrcompat.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/marshal/internal_ivar/depend b/ext/-test-/marshal/internal_ivar/depend index 4fe36834d8..a2e093d809 100644 --- a/ext/-test-/marshal/internal_ivar/depend +++ b/ext/-test-/marshal/internal_ivar/depend @@ -128,6 +128,7 @@ internal_ivar.o: $(hdrdir)/ruby/internal/intern/re.h internal_ivar.o: $(hdrdir)/ruby/internal/intern/ruby.h internal_ivar.o: $(hdrdir)/ruby/internal/intern/select.h internal_ivar.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +internal_ivar.o: $(hdrdir)/ruby/internal/intern/set.h internal_ivar.o: $(hdrdir)/ruby/internal/intern/signal.h internal_ivar.o: $(hdrdir)/ruby/internal/intern/sprintf.h internal_ivar.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ internal_ivar.o: $(hdrdir)/ruby/internal/special_consts.h internal_ivar.o: $(hdrdir)/ruby/internal/static_assert.h internal_ivar.o: $(hdrdir)/ruby/internal/stdalign.h internal_ivar.o: $(hdrdir)/ruby/internal/stdbool.h +internal_ivar.o: $(hdrdir)/ruby/internal/stdckdint.h internal_ivar.o: $(hdrdir)/ruby/internal/symbol.h internal_ivar.o: $(hdrdir)/ruby/internal/value.h internal_ivar.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/marshal/internal_ivar/internal_ivar.c b/ext/-test-/marshal/internal_ivar/internal_ivar.c index b2188f737a..2e2f9cb235 100644 --- a/ext/-test-/marshal/internal_ivar/internal_ivar.c +++ b/ext/-test-/marshal/internal_ivar/internal_ivar.c @@ -1,13 +1,14 @@ #include <ruby.h> -static ID id_normal_ivar, id_internal_ivar, id_encoding_short; +static ID id_normal_ivar, id_internal_ivar, id_encoding_short, id_encoding_long; static VALUE -init(VALUE self, VALUE arg1, VALUE arg2, VALUE arg3) +init(VALUE self, VALUE arg1, VALUE arg2, VALUE arg3, VALUE arg4) { rb_ivar_set(self, id_normal_ivar, arg1); rb_ivar_set(self, id_internal_ivar, arg2); rb_ivar_set(self, id_encoding_short, arg3); + rb_ivar_set(self, id_encoding_long, arg4); return self; } @@ -29,6 +30,12 @@ get_encoding_short(VALUE self) return rb_attr_get(self, id_encoding_short); } +static VALUE +get_encoding_long(VALUE self) +{ + return rb_attr_get(self, id_encoding_long); +} + void Init_internal_ivar(void) { @@ -38,8 +45,10 @@ Init_internal_ivar(void) id_normal_ivar = rb_intern_const("normal"); id_internal_ivar = rb_intern_const("K"); id_encoding_short = rb_intern_const("E"); - rb_define_method(newclass, "initialize", init, 3); + id_encoding_long = rb_intern_const("encoding"); + rb_define_method(newclass, "initialize", init, 4); rb_define_method(newclass, "normal", get_normal, 0); rb_define_method(newclass, "internal", get_internal, 0); rb_define_method(newclass, "encoding_short", get_encoding_short, 0); + rb_define_method(newclass, "encoding_long", get_encoding_long, 0); } diff --git a/ext/-test-/marshal/usr/depend b/ext/-test-/marshal/usr/depend index 1d56cc8202..5ffb8c58de 100644 --- a/ext/-test-/marshal/usr/depend +++ b/ext/-test-/marshal/usr/depend @@ -128,6 +128,7 @@ usrmarshal.o: $(hdrdir)/ruby/internal/intern/re.h usrmarshal.o: $(hdrdir)/ruby/internal/intern/ruby.h usrmarshal.o: $(hdrdir)/ruby/internal/intern/select.h usrmarshal.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +usrmarshal.o: $(hdrdir)/ruby/internal/intern/set.h usrmarshal.o: $(hdrdir)/ruby/internal/intern/signal.h usrmarshal.o: $(hdrdir)/ruby/internal/intern/sprintf.h usrmarshal.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ usrmarshal.o: $(hdrdir)/ruby/internal/special_consts.h usrmarshal.o: $(hdrdir)/ruby/internal/static_assert.h usrmarshal.o: $(hdrdir)/ruby/internal/stdalign.h usrmarshal.o: $(hdrdir)/ruby/internal/stdbool.h +usrmarshal.o: $(hdrdir)/ruby/internal/stdckdint.h usrmarshal.o: $(hdrdir)/ruby/internal/symbol.h usrmarshal.o: $(hdrdir)/ruby/internal/value.h usrmarshal.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/memory_status/depend b/ext/-test-/memory_status/depend index 52e2fe8e1f..4dd503e1bb 100644 --- a/ext/-test-/memory_status/depend +++ b/ext/-test-/memory_status/depend @@ -147,6 +147,7 @@ memory_status.o: $(hdrdir)/ruby/internal/special_consts.h memory_status.o: $(hdrdir)/ruby/internal/static_assert.h memory_status.o: $(hdrdir)/ruby/internal/stdalign.h memory_status.o: $(hdrdir)/ruby/internal/stdbool.h +memory_status.o: $(hdrdir)/ruby/internal/stdckdint.h memory_status.o: $(hdrdir)/ruby/internal/symbol.h memory_status.o: $(hdrdir)/ruby/internal/value.h memory_status.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/memory_view/depend b/ext/-test-/memory_view/depend index c9ef06a15c..a6ffd76f45 100644 --- a/ext/-test-/memory_view/depend +++ b/ext/-test-/memory_view/depend @@ -128,6 +128,7 @@ memory_view.o: $(hdrdir)/ruby/internal/intern/re.h memory_view.o: $(hdrdir)/ruby/internal/intern/ruby.h memory_view.o: $(hdrdir)/ruby/internal/intern/select.h memory_view.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +memory_view.o: $(hdrdir)/ruby/internal/intern/set.h memory_view.o: $(hdrdir)/ruby/internal/intern/signal.h memory_view.o: $(hdrdir)/ruby/internal/intern/sprintf.h memory_view.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ memory_view.o: $(hdrdir)/ruby/internal/special_consts.h memory_view.o: $(hdrdir)/ruby/internal/static_assert.h memory_view.o: $(hdrdir)/ruby/internal/stdalign.h memory_view.o: $(hdrdir)/ruby/internal/stdbool.h +memory_view.o: $(hdrdir)/ruby/internal/stdckdint.h memory_view.o: $(hdrdir)/ruby/internal/symbol.h memory_view.o: $(hdrdir)/ruby/internal/value.h memory_view.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/method/depend b/ext/-test-/method/depend index dbf513f48f..95745b3dae 100644 --- a/ext/-test-/method/depend +++ b/ext/-test-/method/depend @@ -128,6 +128,7 @@ arity.o: $(hdrdir)/ruby/internal/intern/re.h arity.o: $(hdrdir)/ruby/internal/intern/ruby.h arity.o: $(hdrdir)/ruby/internal/intern/select.h arity.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +arity.o: $(hdrdir)/ruby/internal/intern/set.h arity.o: $(hdrdir)/ruby/internal/intern/signal.h arity.o: $(hdrdir)/ruby/internal/intern/sprintf.h arity.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ arity.o: $(hdrdir)/ruby/internal/special_consts.h arity.o: $(hdrdir)/ruby/internal/static_assert.h arity.o: $(hdrdir)/ruby/internal/stdalign.h arity.o: $(hdrdir)/ruby/internal/stdbool.h +arity.o: $(hdrdir)/ruby/internal/stdckdint.h arity.o: $(hdrdir)/ruby/internal/symbol.h arity.o: $(hdrdir)/ruby/internal/value.h arity.o: $(hdrdir)/ruby/internal/value_type.h @@ -287,6 +289,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -306,6 +309,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/notimplement/depend b/ext/-test-/notimplement/depend index 9105093b0d..69c970b6f2 100644 --- a/ext/-test-/notimplement/depend +++ b/ext/-test-/notimplement/depend @@ -128,6 +128,7 @@ bug.o: $(hdrdir)/ruby/internal/intern/re.h bug.o: $(hdrdir)/ruby/internal/intern/ruby.h bug.o: $(hdrdir)/ruby/internal/intern/select.h bug.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +bug.o: $(hdrdir)/ruby/internal/intern/set.h bug.o: $(hdrdir)/ruby/internal/intern/signal.h bug.o: $(hdrdir)/ruby/internal/intern/sprintf.h bug.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ bug.o: $(hdrdir)/ruby/internal/special_consts.h bug.o: $(hdrdir)/ruby/internal/static_assert.h bug.o: $(hdrdir)/ruby/internal/stdalign.h bug.o: $(hdrdir)/ruby/internal/stdbool.h +bug.o: $(hdrdir)/ruby/internal/stdckdint.h bug.o: $(hdrdir)/ruby/internal/symbol.h bug.o: $(hdrdir)/ruby/internal/value.h bug.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/num2int/depend b/ext/-test-/num2int/depend index 4e05d1e8c1..75536363ac 100644 --- a/ext/-test-/num2int/depend +++ b/ext/-test-/num2int/depend @@ -128,6 +128,7 @@ num2int.o: $(hdrdir)/ruby/internal/intern/re.h num2int.o: $(hdrdir)/ruby/internal/intern/ruby.h num2int.o: $(hdrdir)/ruby/internal/intern/select.h num2int.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +num2int.o: $(hdrdir)/ruby/internal/intern/set.h num2int.o: $(hdrdir)/ruby/internal/intern/signal.h num2int.o: $(hdrdir)/ruby/internal/intern/sprintf.h num2int.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ num2int.o: $(hdrdir)/ruby/internal/special_consts.h num2int.o: $(hdrdir)/ruby/internal/static_assert.h num2int.o: $(hdrdir)/ruby/internal/stdalign.h num2int.o: $(hdrdir)/ruby/internal/stdbool.h +num2int.o: $(hdrdir)/ruby/internal/stdckdint.h num2int.o: $(hdrdir)/ruby/internal/symbol.h num2int.o: $(hdrdir)/ruby/internal/value.h num2int.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/path_to_class/depend b/ext/-test-/path_to_class/depend index 8fe6ee37c2..e535058e09 100644 --- a/ext/-test-/path_to_class/depend +++ b/ext/-test-/path_to_class/depend @@ -128,6 +128,7 @@ path_to_class.o: $(hdrdir)/ruby/internal/intern/re.h path_to_class.o: $(hdrdir)/ruby/internal/intern/ruby.h path_to_class.o: $(hdrdir)/ruby/internal/intern/select.h path_to_class.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +path_to_class.o: $(hdrdir)/ruby/internal/intern/set.h path_to_class.o: $(hdrdir)/ruby/internal/intern/signal.h path_to_class.o: $(hdrdir)/ruby/internal/intern/sprintf.h path_to_class.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ path_to_class.o: $(hdrdir)/ruby/internal/special_consts.h path_to_class.o: $(hdrdir)/ruby/internal/static_assert.h path_to_class.o: $(hdrdir)/ruby/internal/stdalign.h path_to_class.o: $(hdrdir)/ruby/internal/stdbool.h +path_to_class.o: $(hdrdir)/ruby/internal/stdckdint.h path_to_class.o: $(hdrdir)/ruby/internal/symbol.h path_to_class.o: $(hdrdir)/ruby/internal/value.h path_to_class.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/popen_deadlock/depend b/ext/-test-/popen_deadlock/depend index fb58ca30e9..0b8932e8b8 100644 --- a/ext/-test-/popen_deadlock/depend +++ b/ext/-test-/popen_deadlock/depend @@ -128,6 +128,7 @@ infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/intern/re.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/intern/ruby.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/intern/select.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/intern/set.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/intern/signal.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/intern/sprintf.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/special_consts.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/static_assert.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/stdalign.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/stdbool.h +infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/stdckdint.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/symbol.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/value.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/postponed_job/depend b/ext/-test-/postponed_job/depend index e44d9d51b7..ff567e3921 100644 --- a/ext/-test-/postponed_job/depend +++ b/ext/-test-/postponed_job/depend @@ -129,6 +129,7 @@ postponed_job.o: $(hdrdir)/ruby/internal/intern/re.h postponed_job.o: $(hdrdir)/ruby/internal/intern/ruby.h postponed_job.o: $(hdrdir)/ruby/internal/intern/select.h postponed_job.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +postponed_job.o: $(hdrdir)/ruby/internal/intern/set.h postponed_job.o: $(hdrdir)/ruby/internal/intern/signal.h postponed_job.o: $(hdrdir)/ruby/internal/intern/sprintf.h postponed_job.o: $(hdrdir)/ruby/internal/intern/string.h @@ -148,6 +149,7 @@ postponed_job.o: $(hdrdir)/ruby/internal/special_consts.h postponed_job.o: $(hdrdir)/ruby/internal/static_assert.h postponed_job.o: $(hdrdir)/ruby/internal/stdalign.h postponed_job.o: $(hdrdir)/ruby/internal/stdbool.h +postponed_job.o: $(hdrdir)/ruby/internal/stdckdint.h postponed_job.o: $(hdrdir)/ruby/internal/symbol.h postponed_job.o: $(hdrdir)/ruby/internal/value.h postponed_job.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/printf/depend b/ext/-test-/printf/depend index b397041103..be895cf769 100644 --- a/ext/-test-/printf/depend +++ b/ext/-test-/printf/depend @@ -138,6 +138,7 @@ printf.o: $(hdrdir)/ruby/internal/intern/re.h printf.o: $(hdrdir)/ruby/internal/intern/ruby.h printf.o: $(hdrdir)/ruby/internal/intern/select.h printf.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +printf.o: $(hdrdir)/ruby/internal/intern/set.h printf.o: $(hdrdir)/ruby/internal/intern/signal.h printf.o: $(hdrdir)/ruby/internal/intern/sprintf.h printf.o: $(hdrdir)/ruby/internal/intern/string.h @@ -157,6 +158,7 @@ printf.o: $(hdrdir)/ruby/internal/special_consts.h printf.o: $(hdrdir)/ruby/internal/static_assert.h printf.o: $(hdrdir)/ruby/internal/stdalign.h printf.o: $(hdrdir)/ruby/internal/stdbool.h +printf.o: $(hdrdir)/ruby/internal/stdckdint.h printf.o: $(hdrdir)/ruby/internal/symbol.h printf.o: $(hdrdir)/ruby/internal/value.h printf.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/proc/depend b/ext/-test-/proc/depend index 7e78aa6f83..97834db0a2 100644 --- a/ext/-test-/proc/depend +++ b/ext/-test-/proc/depend @@ -128,6 +128,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -287,6 +289,7 @@ receiver.o: $(hdrdir)/ruby/internal/intern/re.h receiver.o: $(hdrdir)/ruby/internal/intern/ruby.h receiver.o: $(hdrdir)/ruby/internal/intern/select.h receiver.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +receiver.o: $(hdrdir)/ruby/internal/intern/set.h receiver.o: $(hdrdir)/ruby/internal/intern/signal.h receiver.o: $(hdrdir)/ruby/internal/intern/sprintf.h receiver.o: $(hdrdir)/ruby/internal/intern/string.h @@ -306,6 +309,7 @@ receiver.o: $(hdrdir)/ruby/internal/special_consts.h receiver.o: $(hdrdir)/ruby/internal/static_assert.h receiver.o: $(hdrdir)/ruby/internal/stdalign.h receiver.o: $(hdrdir)/ruby/internal/stdbool.h +receiver.o: $(hdrdir)/ruby/internal/stdckdint.h receiver.o: $(hdrdir)/ruby/internal/symbol.h receiver.o: $(hdrdir)/ruby/internal/value.h receiver.o: $(hdrdir)/ruby/internal/value_type.h @@ -446,6 +450,7 @@ super.o: $(hdrdir)/ruby/internal/intern/re.h super.o: $(hdrdir)/ruby/internal/intern/ruby.h super.o: $(hdrdir)/ruby/internal/intern/select.h super.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +super.o: $(hdrdir)/ruby/internal/intern/set.h super.o: $(hdrdir)/ruby/internal/intern/signal.h super.o: $(hdrdir)/ruby/internal/intern/sprintf.h super.o: $(hdrdir)/ruby/internal/intern/string.h @@ -465,6 +470,7 @@ super.o: $(hdrdir)/ruby/internal/special_consts.h super.o: $(hdrdir)/ruby/internal/static_assert.h super.o: $(hdrdir)/ruby/internal/stdalign.h super.o: $(hdrdir)/ruby/internal/stdbool.h +super.o: $(hdrdir)/ruby/internal/stdckdint.h super.o: $(hdrdir)/ruby/internal/symbol.h super.o: $(hdrdir)/ruby/internal/value.h super.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/public_header_warnings/extconf.rb b/ext/-test-/public_header_warnings/extconf.rb new file mode 100644 index 0000000000..4431e09da4 --- /dev/null +++ b/ext/-test-/public_header_warnings/extconf.rb @@ -0,0 +1,28 @@ +# +# Under compilers with WERRORFLAG, MakeMakefile.try_compile treats warnings as errors, so we can +# use append_cflags to test whether the public header files emit warnings with certain flags turned +# on. +# +def check_append_cflags(flag, msg = nil) + msg ||= "flag #{flag} is not acceptable" + if $CFLAGS.include?(flag) + raise("flag #{flag} already present in $CFLAGS") + end + append_cflags(flag) + unless $CFLAGS.include?(flag) + system("cat mkmf.log") + raise(msg) + end +end + +if %w[gcc clang].include?(RbConfig::CONFIG['CC']) + config_string("WERRORFLAG") or raise("expected WERRORFLAG to be defined") + + # should be acceptable on all modern C compilers + check_append_cflags("-D_TEST_OK", "baseline compiler warning test failed") + + # Feature #20507: Allow compilation of C extensions with -Wconversion + check_append_cflags("-Wconversion", "-Wconversion raising warnings in public headers") +end + +create_makefile("-test-/public_header_warnings") diff --git a/ext/-test-/random/depend b/ext/-test-/random/depend index 3f9a52be44..380c30fbe4 100644 --- a/ext/-test-/random/depend +++ b/ext/-test-/random/depend @@ -127,6 +127,7 @@ bad_version.o: $(hdrdir)/ruby/internal/intern/re.h bad_version.o: $(hdrdir)/ruby/internal/intern/ruby.h bad_version.o: $(hdrdir)/ruby/internal/intern/select.h bad_version.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +bad_version.o: $(hdrdir)/ruby/internal/intern/set.h bad_version.o: $(hdrdir)/ruby/internal/intern/signal.h bad_version.o: $(hdrdir)/ruby/internal/intern/sprintf.h bad_version.o: $(hdrdir)/ruby/internal/intern/string.h @@ -146,6 +147,7 @@ bad_version.o: $(hdrdir)/ruby/internal/special_consts.h bad_version.o: $(hdrdir)/ruby/internal/static_assert.h bad_version.o: $(hdrdir)/ruby/internal/stdalign.h bad_version.o: $(hdrdir)/ruby/internal/stdbool.h +bad_version.o: $(hdrdir)/ruby/internal/stdckdint.h bad_version.o: $(hdrdir)/ruby/internal/symbol.h bad_version.o: $(hdrdir)/ruby/internal/value.h bad_version.o: $(hdrdir)/ruby/internal/value_type.h @@ -287,6 +289,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -306,6 +309,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -445,6 +449,7 @@ loop.o: $(hdrdir)/ruby/internal/intern/re.h loop.o: $(hdrdir)/ruby/internal/intern/ruby.h loop.o: $(hdrdir)/ruby/internal/intern/select.h loop.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +loop.o: $(hdrdir)/ruby/internal/intern/set.h loop.o: $(hdrdir)/ruby/internal/intern/signal.h loop.o: $(hdrdir)/ruby/internal/intern/sprintf.h loop.o: $(hdrdir)/ruby/internal/intern/string.h @@ -464,6 +469,7 @@ loop.o: $(hdrdir)/ruby/internal/special_consts.h loop.o: $(hdrdir)/ruby/internal/static_assert.h loop.o: $(hdrdir)/ruby/internal/stdalign.h loop.o: $(hdrdir)/ruby/internal/stdbool.h +loop.o: $(hdrdir)/ruby/internal/stdckdint.h loop.o: $(hdrdir)/ruby/internal/symbol.h loop.o: $(hdrdir)/ruby/internal/value.h loop.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/random/loop.c b/ext/-test-/random/loop.c index b789ab1d01..f79e5cfd83 100644 --- a/ext/-test-/random/loop.c +++ b/ext/-test-/random/loop.c @@ -13,6 +13,15 @@ static const rb_random_interface_t random_loop_if = { RB_RANDOM_INTERFACE_DEFINE_WITH_REAL(loop) }; +static void +loop_free(void *ptr) +{ + rand_loop_t *r = ptr; + + xfree(r->buf); + xfree(r); +} + RB_RANDOM_DEFINE_INIT_INT32_FUNC(loop) static size_t random_loop_memsize(const void *ptr) @@ -25,7 +34,7 @@ static rb_random_data_type_t random_loop_type = { "random/loop", { rb_random_mark, - RUBY_TYPED_DEFAULT_FREE, + loop_free, random_loop_memsize, }, RB_RANDOM_PARENT, diff --git a/ext/-test-/rational/depend b/ext/-test-/rational/depend index cff2eae38d..56d6ab77d6 100644 --- a/ext/-test-/rational/depend +++ b/ext/-test-/rational/depend @@ -132,6 +132,7 @@ rat.o: $(hdrdir)/ruby/internal/intern/re.h rat.o: $(hdrdir)/ruby/internal/intern/ruby.h rat.o: $(hdrdir)/ruby/internal/intern/select.h rat.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +rat.o: $(hdrdir)/ruby/internal/intern/set.h rat.o: $(hdrdir)/ruby/internal/intern/signal.h rat.o: $(hdrdir)/ruby/internal/intern/sprintf.h rat.o: $(hdrdir)/ruby/internal/intern/string.h @@ -151,6 +152,7 @@ rat.o: $(hdrdir)/ruby/internal/special_consts.h rat.o: $(hdrdir)/ruby/internal/static_assert.h rat.o: $(hdrdir)/ruby/internal/stdalign.h rat.o: $(hdrdir)/ruby/internal/stdbool.h +rat.o: $(hdrdir)/ruby/internal/stdckdint.h rat.o: $(hdrdir)/ruby/internal/symbol.h rat.o: $(hdrdir)/ruby/internal/value.h rat.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/rb_call_super_kw/depend b/ext/-test-/rb_call_super_kw/depend index a42ddc85ac..bf34323ca7 100644 --- a/ext/-test-/rb_call_super_kw/depend +++ b/ext/-test-/rb_call_super_kw/depend @@ -128,6 +128,7 @@ rb_call_super_kw.o: $(hdrdir)/ruby/internal/intern/re.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/intern/ruby.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/intern/select.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +rb_call_super_kw.o: $(hdrdir)/ruby/internal/intern/set.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/intern/signal.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/intern/sprintf.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ rb_call_super_kw.o: $(hdrdir)/ruby/internal/special_consts.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/static_assert.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/stdalign.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/stdbool.h +rb_call_super_kw.o: $(hdrdir)/ruby/internal/stdckdint.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/symbol.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/value.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/recursion/depend b/ext/-test-/recursion/depend index 49377250ef..b6487eb4df 100644 --- a/ext/-test-/recursion/depend +++ b/ext/-test-/recursion/depend @@ -128,6 +128,7 @@ recursion.o: $(hdrdir)/ruby/internal/intern/re.h recursion.o: $(hdrdir)/ruby/internal/intern/ruby.h recursion.o: $(hdrdir)/ruby/internal/intern/select.h recursion.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +recursion.o: $(hdrdir)/ruby/internal/intern/set.h recursion.o: $(hdrdir)/ruby/internal/intern/signal.h recursion.o: $(hdrdir)/ruby/internal/intern/sprintf.h recursion.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ recursion.o: $(hdrdir)/ruby/internal/special_consts.h recursion.o: $(hdrdir)/ruby/internal/static_assert.h recursion.o: $(hdrdir)/ruby/internal/stdalign.h recursion.o: $(hdrdir)/ruby/internal/stdbool.h +recursion.o: $(hdrdir)/ruby/internal/stdckdint.h recursion.o: $(hdrdir)/ruby/internal/symbol.h recursion.o: $(hdrdir)/ruby/internal/value.h recursion.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/regexp/depend b/ext/-test-/regexp/depend index 484f0320dd..5ba1b92f18 100644 --- a/ext/-test-/regexp/depend +++ b/ext/-test-/regexp/depend @@ -128,6 +128,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -287,6 +289,7 @@ parse_depth_limit.o: $(hdrdir)/ruby/internal/intern/re.h parse_depth_limit.o: $(hdrdir)/ruby/internal/intern/ruby.h parse_depth_limit.o: $(hdrdir)/ruby/internal/intern/select.h parse_depth_limit.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +parse_depth_limit.o: $(hdrdir)/ruby/internal/intern/set.h parse_depth_limit.o: $(hdrdir)/ruby/internal/intern/signal.h parse_depth_limit.o: $(hdrdir)/ruby/internal/intern/sprintf.h parse_depth_limit.o: $(hdrdir)/ruby/internal/intern/string.h @@ -306,6 +309,7 @@ parse_depth_limit.o: $(hdrdir)/ruby/internal/special_consts.h parse_depth_limit.o: $(hdrdir)/ruby/internal/static_assert.h parse_depth_limit.o: $(hdrdir)/ruby/internal/stdalign.h parse_depth_limit.o: $(hdrdir)/ruby/internal/stdbool.h +parse_depth_limit.o: $(hdrdir)/ruby/internal/stdckdint.h parse_depth_limit.o: $(hdrdir)/ruby/internal/symbol.h parse_depth_limit.o: $(hdrdir)/ruby/internal/value.h parse_depth_limit.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/sanitizers/depend b/ext/-test-/sanitizers/depend new file mode 100644 index 0000000000..0e6e632803 --- /dev/null +++ b/ext/-test-/sanitizers/depend @@ -0,0 +1,162 @@ +# AUTOGENERATED DEPENDENCIES START +sanitizers.o: $(RUBY_EXTCONF_H) +sanitizers.o: $(arch_hdrdir)/ruby/config.h +sanitizers.o: $(hdrdir)/ruby/assert.h +sanitizers.o: $(hdrdir)/ruby/backward.h +sanitizers.o: $(hdrdir)/ruby/backward/2/assume.h +sanitizers.o: $(hdrdir)/ruby/backward/2/attributes.h +sanitizers.o: $(hdrdir)/ruby/backward/2/bool.h +sanitizers.o: $(hdrdir)/ruby/backward/2/inttypes.h +sanitizers.o: $(hdrdir)/ruby/backward/2/limits.h +sanitizers.o: $(hdrdir)/ruby/backward/2/long_long.h +sanitizers.o: $(hdrdir)/ruby/backward/2/stdalign.h +sanitizers.o: $(hdrdir)/ruby/backward/2/stdarg.h +sanitizers.o: $(hdrdir)/ruby/defines.h +sanitizers.o: $(hdrdir)/ruby/intern.h +sanitizers.o: $(hdrdir)/ruby/internal/abi.h +sanitizers.o: $(hdrdir)/ruby/internal/anyargs.h +sanitizers.o: $(hdrdir)/ruby/internal/arithmetic.h +sanitizers.o: $(hdrdir)/ruby/internal/arithmetic/char.h +sanitizers.o: $(hdrdir)/ruby/internal/arithmetic/double.h +sanitizers.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +sanitizers.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +sanitizers.o: $(hdrdir)/ruby/internal/arithmetic/int.h +sanitizers.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +sanitizers.o: $(hdrdir)/ruby/internal/arithmetic/long.h +sanitizers.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +sanitizers.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +sanitizers.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +sanitizers.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +sanitizers.o: $(hdrdir)/ruby/internal/arithmetic/short.h +sanitizers.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +sanitizers.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +sanitizers.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +sanitizers.o: $(hdrdir)/ruby/internal/assume.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/artificial.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/cold.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/const.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/constexpr.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/deprecated.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/error.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/forceinline.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/format.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/noalias.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/noexcept.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/noinline.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/nonnull.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/noreturn.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/pure.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/restrict.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/warning.h +sanitizers.o: $(hdrdir)/ruby/internal/attr/weakref.h +sanitizers.o: $(hdrdir)/ruby/internal/cast.h +sanitizers.o: $(hdrdir)/ruby/internal/compiler_is.h +sanitizers.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +sanitizers.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +sanitizers.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +sanitizers.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +sanitizers.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +sanitizers.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +sanitizers.o: $(hdrdir)/ruby/internal/compiler_since.h +sanitizers.o: $(hdrdir)/ruby/internal/config.h +sanitizers.o: $(hdrdir)/ruby/internal/constant_p.h +sanitizers.o: $(hdrdir)/ruby/internal/core.h +sanitizers.o: $(hdrdir)/ruby/internal/core/rarray.h +sanitizers.o: $(hdrdir)/ruby/internal/core/rbasic.h +sanitizers.o: $(hdrdir)/ruby/internal/core/rbignum.h +sanitizers.o: $(hdrdir)/ruby/internal/core/rclass.h +sanitizers.o: $(hdrdir)/ruby/internal/core/rdata.h +sanitizers.o: $(hdrdir)/ruby/internal/core/rfile.h +sanitizers.o: $(hdrdir)/ruby/internal/core/rhash.h +sanitizers.o: $(hdrdir)/ruby/internal/core/robject.h +sanitizers.o: $(hdrdir)/ruby/internal/core/rregexp.h +sanitizers.o: $(hdrdir)/ruby/internal/core/rstring.h +sanitizers.o: $(hdrdir)/ruby/internal/core/rstruct.h +sanitizers.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +sanitizers.o: $(hdrdir)/ruby/internal/ctype.h +sanitizers.o: $(hdrdir)/ruby/internal/dllexport.h +sanitizers.o: $(hdrdir)/ruby/internal/dosish.h +sanitizers.o: $(hdrdir)/ruby/internal/error.h +sanitizers.o: $(hdrdir)/ruby/internal/eval.h +sanitizers.o: $(hdrdir)/ruby/internal/event.h +sanitizers.o: $(hdrdir)/ruby/internal/fl_type.h +sanitizers.o: $(hdrdir)/ruby/internal/gc.h +sanitizers.o: $(hdrdir)/ruby/internal/glob.h +sanitizers.o: $(hdrdir)/ruby/internal/globals.h +sanitizers.o: $(hdrdir)/ruby/internal/has/attribute.h +sanitizers.o: $(hdrdir)/ruby/internal/has/builtin.h +sanitizers.o: $(hdrdir)/ruby/internal/has/c_attribute.h +sanitizers.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +sanitizers.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +sanitizers.o: $(hdrdir)/ruby/internal/has/extension.h +sanitizers.o: $(hdrdir)/ruby/internal/has/feature.h +sanitizers.o: $(hdrdir)/ruby/internal/has/warning.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/array.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/bignum.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/class.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/compar.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/complex.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/cont.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/dir.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/enum.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/enumerator.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/error.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/eval.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/file.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/hash.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/io.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/load.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/marshal.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/numeric.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/object.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/parse.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/proc.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/process.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/random.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/range.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/rational.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/re.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/ruby.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/select.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/set.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/signal.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/sprintf.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/string.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/struct.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/thread.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/time.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/variable.h +sanitizers.o: $(hdrdir)/ruby/internal/intern/vm.h +sanitizers.o: $(hdrdir)/ruby/internal/interpreter.h +sanitizers.o: $(hdrdir)/ruby/internal/iterator.h +sanitizers.o: $(hdrdir)/ruby/internal/memory.h +sanitizers.o: $(hdrdir)/ruby/internal/method.h +sanitizers.o: $(hdrdir)/ruby/internal/module.h +sanitizers.o: $(hdrdir)/ruby/internal/newobj.h +sanitizers.o: $(hdrdir)/ruby/internal/scan_args.h +sanitizers.o: $(hdrdir)/ruby/internal/special_consts.h +sanitizers.o: $(hdrdir)/ruby/internal/static_assert.h +sanitizers.o: $(hdrdir)/ruby/internal/stdalign.h +sanitizers.o: $(hdrdir)/ruby/internal/stdbool.h +sanitizers.o: $(hdrdir)/ruby/internal/stdckdint.h +sanitizers.o: $(hdrdir)/ruby/internal/symbol.h +sanitizers.o: $(hdrdir)/ruby/internal/value.h +sanitizers.o: $(hdrdir)/ruby/internal/value_type.h +sanitizers.o: $(hdrdir)/ruby/internal/variable.h +sanitizers.o: $(hdrdir)/ruby/internal/warning_push.h +sanitizers.o: $(hdrdir)/ruby/internal/xmalloc.h +sanitizers.o: $(hdrdir)/ruby/missing.h +sanitizers.o: $(hdrdir)/ruby/ruby.h +sanitizers.o: $(hdrdir)/ruby/st.h +sanitizers.o: $(hdrdir)/ruby/subst.h +sanitizers.o: sanitizers.c +# AUTOGENERATED DEPENDENCIES END diff --git a/ext/-test-/sanitizers/extconf.rb b/ext/-test-/sanitizers/extconf.rb new file mode 100644 index 0000000000..c94a96de6c --- /dev/null +++ b/ext/-test-/sanitizers/extconf.rb @@ -0,0 +1,2 @@ +require 'mkmf' +create_makefile('-test-/sanitizers') diff --git a/ext/-test-/sanitizers/sanitizers.c b/ext/-test-/sanitizers/sanitizers.c new file mode 100644 index 0000000000..97a85b26ef --- /dev/null +++ b/ext/-test-/sanitizers/sanitizers.c @@ -0,0 +1,36 @@ +#include "ruby/ruby.h" + +static VALUE +asan_enabled_p(VALUE self) +{ +#if defined(__has_feature) + /* clang uses __has_feature for determining asan */ + return __has_feature(address_sanitizer) ? Qtrue : Qfalse; +#elif defined(__SANITIZE_ADDRESS__) + /* GCC sets __SANITIZE_ADDRESS__ for determining asan */ + return Qtrue; +#else + return Qfalse; +#endif +} + +static VALUE +lsan_enabled_p(VALUE self) +{ +#if defined(__has_feature) + /* clang uses __has_feature for determining LSAN */ + return __has_feature(leak_sanitizer) ? Qtrue : Qfalse; +#else + return Qfalse; +#endif +} + +void +Init_sanitizers(void) +{ + VALUE m = rb_define_module("Test"); + VALUE c = rb_define_class_under(m, "Sanitizers", rb_cObject); + rb_define_singleton_method(c, "asan_enabled?", asan_enabled_p, 0); + rb_define_singleton_method(c, "lsan_enabled?", lsan_enabled_p, 0); +} + diff --git a/ext/-test-/scan_args/depend b/ext/-test-/scan_args/depend index 3bedc6a7cf..ca0fc19238 100644 --- a/ext/-test-/scan_args/depend +++ b/ext/-test-/scan_args/depend @@ -128,6 +128,7 @@ scan_args.o: $(hdrdir)/ruby/internal/intern/re.h scan_args.o: $(hdrdir)/ruby/internal/intern/ruby.h scan_args.o: $(hdrdir)/ruby/internal/intern/select.h scan_args.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +scan_args.o: $(hdrdir)/ruby/internal/intern/set.h scan_args.o: $(hdrdir)/ruby/internal/intern/signal.h scan_args.o: $(hdrdir)/ruby/internal/intern/sprintf.h scan_args.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ scan_args.o: $(hdrdir)/ruby/internal/special_consts.h scan_args.o: $(hdrdir)/ruby/internal/static_assert.h scan_args.o: $(hdrdir)/ruby/internal/stdalign.h scan_args.o: $(hdrdir)/ruby/internal/stdbool.h +scan_args.o: $(hdrdir)/ruby/internal/stdckdint.h scan_args.o: $(hdrdir)/ruby/internal/symbol.h scan_args.o: $(hdrdir)/ruby/internal/value.h scan_args.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/scheduler/extconf.rb b/ext/-test-/scheduler/extconf.rb new file mode 100644 index 0000000000..159699bd8e --- /dev/null +++ b/ext/-test-/scheduler/extconf.rb @@ -0,0 +1,2 @@ +# frozen_string_literal: false +create_makefile("-test-/scheduler") diff --git a/ext/-test-/scheduler/scheduler.c b/ext/-test-/scheduler/scheduler.c new file mode 100644 index 0000000000..b742a5573b --- /dev/null +++ b/ext/-test-/scheduler/scheduler.c @@ -0,0 +1,92 @@ +#include "ruby/ruby.h" +#include "ruby/thread.h" +#include "ruby/io.h" +#include "ruby/fiber/scheduler.h" + +/* + * Test extension for reproducing the gRPC interrupt handling bug. + * + * This reproduces the exact issue from grpc/grpc commit 69f229e (June 2025): + * https://github.com/grpc/grpc/commit/69f229edd1d79ab7a7dfda98e3aef6fd807adcad + * + * The bug occurs when: + * 1. A fiber scheduler uses Thread.handle_interrupt(::SignalException => :never) + * (like Async::Scheduler does) + * 2. Native code uses rb_thread_call_without_gvl in a retry loop that checks + * the interrupted flag and retries (like gRPC's completion queue) + * 3. A signal (SIGINT/SIGTERM) is sent + * 4. The unblock_func sets interrupted=1, but Thread.handle_interrupt defers the signal + * 5. The loop sees interrupted=1 and retries without yielding to the scheduler + * 6. The deferred interrupt never gets processed -> infinite hang + * + * The fix is in vm_check_ints_blocking() in thread.c, which should yield to + * the fiber scheduler when interrupts are pending, allowing the scheduler to + * detect Thread.pending_interrupt? and exit its run loop. + */ + +struct blocking_state { + int notify_descriptor; + volatile int interrupted; +}; + +static void +unblock_callback(void *argument) +{ + struct blocking_state *blocking_state = (struct blocking_state *)argument; + blocking_state->interrupted = 1; +} + +static void * +blocking_operation(void *argument) +{ + struct blocking_state *blocking_state = (struct blocking_state *)argument; + + ssize_t ret = write(blocking_state->notify_descriptor, "x", 1); + (void)ret; // ignore the result for now + + while (!blocking_state->interrupted) { + struct timeval tv = {1, 0}; // 1 second timeout. + int result = select(0, NULL, NULL, NULL, &tv); + + if (result == -1 && errno == EINTR) { + blocking_state->interrupted = 1; + } + + // Otherwise, timeout -> loop again. + } + + return NULL; +} + +static VALUE +scheduler_blocking_loop(VALUE self, VALUE notify) +{ + struct blocking_state blocking_state = { + .notify_descriptor = rb_io_descriptor(notify), + .interrupted = 0, + }; + + while (true) { + blocking_state.interrupted = 0; + + rb_thread_call_without_gvl( + blocking_operation, &blocking_state, + unblock_callback, &blocking_state + ); + + // The bug: When interrupted, loop retries without yielding to scheduler. + // With Thread.handle_interrupt(:never), this causes an infinite hang, + // because the deferred interrupt never gets a chance to be processed. + } while (blocking_state.interrupted); + + return Qnil; +} + +void +Init_scheduler(void) +{ + VALUE mBug = rb_define_module("Bug"); + VALUE mScheduler = rb_define_module_under(mBug, "Scheduler"); + + rb_define_module_function(mScheduler, "blocking_loop", scheduler_blocking_loop, 1); +} diff --git a/ext/-test-/st/foreach/depend b/ext/-test-/st/foreach/depend index fdfe356805..29aab2bb29 100644 --- a/ext/-test-/st/foreach/depend +++ b/ext/-test-/st/foreach/depend @@ -128,6 +128,7 @@ foreach.o: $(hdrdir)/ruby/internal/intern/re.h foreach.o: $(hdrdir)/ruby/internal/intern/ruby.h foreach.o: $(hdrdir)/ruby/internal/intern/select.h foreach.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +foreach.o: $(hdrdir)/ruby/internal/intern/set.h foreach.o: $(hdrdir)/ruby/internal/intern/signal.h foreach.o: $(hdrdir)/ruby/internal/intern/sprintf.h foreach.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ foreach.o: $(hdrdir)/ruby/internal/special_consts.h foreach.o: $(hdrdir)/ruby/internal/static_assert.h foreach.o: $(hdrdir)/ruby/internal/stdalign.h foreach.o: $(hdrdir)/ruby/internal/stdbool.h +foreach.o: $(hdrdir)/ruby/internal/stdckdint.h foreach.o: $(hdrdir)/ruby/internal/symbol.h foreach.o: $(hdrdir)/ruby/internal/value.h foreach.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/st/numhash/depend b/ext/-test-/st/numhash/depend index ef28c892f3..18320d55f5 100644 --- a/ext/-test-/st/numhash/depend +++ b/ext/-test-/st/numhash/depend @@ -128,6 +128,7 @@ numhash.o: $(hdrdir)/ruby/internal/intern/re.h numhash.o: $(hdrdir)/ruby/internal/intern/ruby.h numhash.o: $(hdrdir)/ruby/internal/intern/select.h numhash.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +numhash.o: $(hdrdir)/ruby/internal/intern/set.h numhash.o: $(hdrdir)/ruby/internal/intern/signal.h numhash.o: $(hdrdir)/ruby/internal/intern/sprintf.h numhash.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ numhash.o: $(hdrdir)/ruby/internal/special_consts.h numhash.o: $(hdrdir)/ruby/internal/static_assert.h numhash.o: $(hdrdir)/ruby/internal/stdalign.h numhash.o: $(hdrdir)/ruby/internal/stdbool.h +numhash.o: $(hdrdir)/ruby/internal/stdckdint.h numhash.o: $(hdrdir)/ruby/internal/symbol.h numhash.o: $(hdrdir)/ruby/internal/value.h numhash.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/st/update/depend b/ext/-test-/st/update/depend index 2d5ff224a2..247f0efd6b 100644 --- a/ext/-test-/st/update/depend +++ b/ext/-test-/st/update/depend @@ -128,6 +128,7 @@ update.o: $(hdrdir)/ruby/internal/intern/re.h update.o: $(hdrdir)/ruby/internal/intern/ruby.h update.o: $(hdrdir)/ruby/internal/intern/select.h update.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +update.o: $(hdrdir)/ruby/internal/intern/set.h update.o: $(hdrdir)/ruby/internal/intern/signal.h update.o: $(hdrdir)/ruby/internal/intern/sprintf.h update.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ update.o: $(hdrdir)/ruby/internal/special_consts.h update.o: $(hdrdir)/ruby/internal/static_assert.h update.o: $(hdrdir)/ruby/internal/stdalign.h update.o: $(hdrdir)/ruby/internal/stdbool.h +update.o: $(hdrdir)/ruby/internal/stdckdint.h update.o: $(hdrdir)/ruby/internal/symbol.h update.o: $(hdrdir)/ruby/internal/value.h update.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/stack/depend b/ext/-test-/stack/depend new file mode 100644 index 0000000000..77e93bb201 --- /dev/null +++ b/ext/-test-/stack/depend @@ -0,0 +1,179 @@ +# AUTOGENERATED DEPENDENCIES START +stack.o: $(RUBY_EXTCONF_H) +stack.o: $(arch_hdrdir)/ruby/config.h +stack.o: $(hdrdir)/ruby.h +stack.o: $(hdrdir)/ruby/assert.h +stack.o: $(hdrdir)/ruby/backward.h +stack.o: $(hdrdir)/ruby/backward/2/assume.h +stack.o: $(hdrdir)/ruby/backward/2/attributes.h +stack.o: $(hdrdir)/ruby/backward/2/bool.h +stack.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +stack.o: $(hdrdir)/ruby/backward/2/inttypes.h +stack.o: $(hdrdir)/ruby/backward/2/limits.h +stack.o: $(hdrdir)/ruby/backward/2/long_long.h +stack.o: $(hdrdir)/ruby/backward/2/stdalign.h +stack.o: $(hdrdir)/ruby/backward/2/stdarg.h +stack.o: $(hdrdir)/ruby/defines.h +stack.o: $(hdrdir)/ruby/encoding.h +stack.o: $(hdrdir)/ruby/intern.h +stack.o: $(hdrdir)/ruby/internal/abi.h +stack.o: $(hdrdir)/ruby/internal/anyargs.h +stack.o: $(hdrdir)/ruby/internal/arithmetic.h +stack.o: $(hdrdir)/ruby/internal/arithmetic/char.h +stack.o: $(hdrdir)/ruby/internal/arithmetic/double.h +stack.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +stack.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +stack.o: $(hdrdir)/ruby/internal/arithmetic/int.h +stack.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +stack.o: $(hdrdir)/ruby/internal/arithmetic/long.h +stack.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +stack.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +stack.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +stack.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +stack.o: $(hdrdir)/ruby/internal/arithmetic/short.h +stack.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +stack.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +stack.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +stack.o: $(hdrdir)/ruby/internal/assume.h +stack.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +stack.o: $(hdrdir)/ruby/internal/attr/artificial.h +stack.o: $(hdrdir)/ruby/internal/attr/cold.h +stack.o: $(hdrdir)/ruby/internal/attr/const.h +stack.o: $(hdrdir)/ruby/internal/attr/constexpr.h +stack.o: $(hdrdir)/ruby/internal/attr/deprecated.h +stack.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +stack.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +stack.o: $(hdrdir)/ruby/internal/attr/error.h +stack.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +stack.o: $(hdrdir)/ruby/internal/attr/forceinline.h +stack.o: $(hdrdir)/ruby/internal/attr/format.h +stack.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +stack.o: $(hdrdir)/ruby/internal/attr/noalias.h +stack.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +stack.o: $(hdrdir)/ruby/internal/attr/noexcept.h +stack.o: $(hdrdir)/ruby/internal/attr/noinline.h +stack.o: $(hdrdir)/ruby/internal/attr/nonnull.h +stack.o: $(hdrdir)/ruby/internal/attr/noreturn.h +stack.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +stack.o: $(hdrdir)/ruby/internal/attr/pure.h +stack.o: $(hdrdir)/ruby/internal/attr/restrict.h +stack.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +stack.o: $(hdrdir)/ruby/internal/attr/warning.h +stack.o: $(hdrdir)/ruby/internal/attr/weakref.h +stack.o: $(hdrdir)/ruby/internal/cast.h +stack.o: $(hdrdir)/ruby/internal/compiler_is.h +stack.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +stack.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +stack.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +stack.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +stack.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +stack.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +stack.o: $(hdrdir)/ruby/internal/compiler_since.h +stack.o: $(hdrdir)/ruby/internal/config.h +stack.o: $(hdrdir)/ruby/internal/constant_p.h +stack.o: $(hdrdir)/ruby/internal/core.h +stack.o: $(hdrdir)/ruby/internal/core/rarray.h +stack.o: $(hdrdir)/ruby/internal/core/rbasic.h +stack.o: $(hdrdir)/ruby/internal/core/rbignum.h +stack.o: $(hdrdir)/ruby/internal/core/rclass.h +stack.o: $(hdrdir)/ruby/internal/core/rdata.h +stack.o: $(hdrdir)/ruby/internal/core/rfile.h +stack.o: $(hdrdir)/ruby/internal/core/rhash.h +stack.o: $(hdrdir)/ruby/internal/core/robject.h +stack.o: $(hdrdir)/ruby/internal/core/rregexp.h +stack.o: $(hdrdir)/ruby/internal/core/rstring.h +stack.o: $(hdrdir)/ruby/internal/core/rstruct.h +stack.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +stack.o: $(hdrdir)/ruby/internal/ctype.h +stack.o: $(hdrdir)/ruby/internal/dllexport.h +stack.o: $(hdrdir)/ruby/internal/dosish.h +stack.o: $(hdrdir)/ruby/internal/encoding/coderange.h +stack.o: $(hdrdir)/ruby/internal/encoding/ctype.h +stack.o: $(hdrdir)/ruby/internal/encoding/encoding.h +stack.o: $(hdrdir)/ruby/internal/encoding/pathname.h +stack.o: $(hdrdir)/ruby/internal/encoding/re.h +stack.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +stack.o: $(hdrdir)/ruby/internal/encoding/string.h +stack.o: $(hdrdir)/ruby/internal/encoding/symbol.h +stack.o: $(hdrdir)/ruby/internal/encoding/transcode.h +stack.o: $(hdrdir)/ruby/internal/error.h +stack.o: $(hdrdir)/ruby/internal/eval.h +stack.o: $(hdrdir)/ruby/internal/event.h +stack.o: $(hdrdir)/ruby/internal/fl_type.h +stack.o: $(hdrdir)/ruby/internal/gc.h +stack.o: $(hdrdir)/ruby/internal/glob.h +stack.o: $(hdrdir)/ruby/internal/globals.h +stack.o: $(hdrdir)/ruby/internal/has/attribute.h +stack.o: $(hdrdir)/ruby/internal/has/builtin.h +stack.o: $(hdrdir)/ruby/internal/has/c_attribute.h +stack.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +stack.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +stack.o: $(hdrdir)/ruby/internal/has/extension.h +stack.o: $(hdrdir)/ruby/internal/has/feature.h +stack.o: $(hdrdir)/ruby/internal/has/warning.h +stack.o: $(hdrdir)/ruby/internal/intern/array.h +stack.o: $(hdrdir)/ruby/internal/intern/bignum.h +stack.o: $(hdrdir)/ruby/internal/intern/class.h +stack.o: $(hdrdir)/ruby/internal/intern/compar.h +stack.o: $(hdrdir)/ruby/internal/intern/complex.h +stack.o: $(hdrdir)/ruby/internal/intern/cont.h +stack.o: $(hdrdir)/ruby/internal/intern/dir.h +stack.o: $(hdrdir)/ruby/internal/intern/enum.h +stack.o: $(hdrdir)/ruby/internal/intern/enumerator.h +stack.o: $(hdrdir)/ruby/internal/intern/error.h +stack.o: $(hdrdir)/ruby/internal/intern/eval.h +stack.o: $(hdrdir)/ruby/internal/intern/file.h +stack.o: $(hdrdir)/ruby/internal/intern/hash.h +stack.o: $(hdrdir)/ruby/internal/intern/io.h +stack.o: $(hdrdir)/ruby/internal/intern/load.h +stack.o: $(hdrdir)/ruby/internal/intern/marshal.h +stack.o: $(hdrdir)/ruby/internal/intern/numeric.h +stack.o: $(hdrdir)/ruby/internal/intern/object.h +stack.o: $(hdrdir)/ruby/internal/intern/parse.h +stack.o: $(hdrdir)/ruby/internal/intern/proc.h +stack.o: $(hdrdir)/ruby/internal/intern/process.h +stack.o: $(hdrdir)/ruby/internal/intern/random.h +stack.o: $(hdrdir)/ruby/internal/intern/range.h +stack.o: $(hdrdir)/ruby/internal/intern/rational.h +stack.o: $(hdrdir)/ruby/internal/intern/re.h +stack.o: $(hdrdir)/ruby/internal/intern/ruby.h +stack.o: $(hdrdir)/ruby/internal/intern/select.h +stack.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +stack.o: $(hdrdir)/ruby/internal/intern/set.h +stack.o: $(hdrdir)/ruby/internal/intern/signal.h +stack.o: $(hdrdir)/ruby/internal/intern/sprintf.h +stack.o: $(hdrdir)/ruby/internal/intern/string.h +stack.o: $(hdrdir)/ruby/internal/intern/struct.h +stack.o: $(hdrdir)/ruby/internal/intern/thread.h +stack.o: $(hdrdir)/ruby/internal/intern/time.h +stack.o: $(hdrdir)/ruby/internal/intern/variable.h +stack.o: $(hdrdir)/ruby/internal/intern/vm.h +stack.o: $(hdrdir)/ruby/internal/interpreter.h +stack.o: $(hdrdir)/ruby/internal/iterator.h +stack.o: $(hdrdir)/ruby/internal/memory.h +stack.o: $(hdrdir)/ruby/internal/method.h +stack.o: $(hdrdir)/ruby/internal/module.h +stack.o: $(hdrdir)/ruby/internal/newobj.h +stack.o: $(hdrdir)/ruby/internal/scan_args.h +stack.o: $(hdrdir)/ruby/internal/special_consts.h +stack.o: $(hdrdir)/ruby/internal/static_assert.h +stack.o: $(hdrdir)/ruby/internal/stdalign.h +stack.o: $(hdrdir)/ruby/internal/stdbool.h +stack.o: $(hdrdir)/ruby/internal/stdckdint.h +stack.o: $(hdrdir)/ruby/internal/symbol.h +stack.o: $(hdrdir)/ruby/internal/value.h +stack.o: $(hdrdir)/ruby/internal/value_type.h +stack.o: $(hdrdir)/ruby/internal/variable.h +stack.o: $(hdrdir)/ruby/internal/warning_push.h +stack.o: $(hdrdir)/ruby/internal/xmalloc.h +stack.o: $(hdrdir)/ruby/missing.h +stack.o: $(hdrdir)/ruby/onigmo.h +stack.o: $(hdrdir)/ruby/oniguruma.h +stack.o: $(hdrdir)/ruby/ruby.h +stack.o: $(hdrdir)/ruby/st.h +stack.o: $(hdrdir)/ruby/subst.h +stack.o: $(top_srcdir)/encindex.h +stack.o: $(top_srcdir)/internal/compilers.h +stack.o: $(top_srcdir)/internal/string.h +stack.o: stack.c +# AUTOGENERATED DEPENDENCIES END diff --git a/ext/-test-/stack/extconf.rb b/ext/-test-/stack/extconf.rb new file mode 100644 index 0000000000..d786b15db9 --- /dev/null +++ b/ext/-test-/stack/extconf.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: false +require_relative "../auto_ext.rb" +auto_ext(inc: true) diff --git a/ext/-test-/stack/stack.c b/ext/-test-/stack/stack.c new file mode 100644 index 0000000000..f0e65e74b2 --- /dev/null +++ b/ext/-test-/stack/stack.c @@ -0,0 +1,35 @@ +#include "ruby.h" +#include "internal/string.h" + +static VALUE +stack_overflow(VALUE self) +{ + size_t i = 0; + + while (1) { + // Allocate and touch memory to force actual stack usage: + volatile char *stack = alloca(1024); + stack[0] = (char)i; + stack[1023] = (char)i; + i++; + } + + return Qnil; +} + +static VALUE +asan_p(VALUE klass) +{ +#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer) + return Qtrue; +#else + return Qfalse; +#endif +} + +void +Init_stack(VALUE klass) +{ + rb_define_singleton_method(rb_cThread, "stack_overflow", stack_overflow, 0); + rb_define_singleton_method(rb_cThread, "asan?", asan_p, 0); +} diff --git a/ext/-test-/string/depend b/ext/-test-/string/depend index eeb4914346..478ae3b82b 100644 --- a/ext/-test-/string/depend +++ b/ext/-test-/string/depend @@ -139,6 +139,7 @@ capacity.o: $(hdrdir)/ruby/internal/intern/re.h capacity.o: $(hdrdir)/ruby/internal/intern/ruby.h capacity.o: $(hdrdir)/ruby/internal/intern/select.h capacity.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +capacity.o: $(hdrdir)/ruby/internal/intern/set.h capacity.o: $(hdrdir)/ruby/internal/intern/signal.h capacity.o: $(hdrdir)/ruby/internal/intern/sprintf.h capacity.o: $(hdrdir)/ruby/internal/intern/string.h @@ -158,6 +159,7 @@ capacity.o: $(hdrdir)/ruby/internal/special_consts.h capacity.o: $(hdrdir)/ruby/internal/static_assert.h capacity.o: $(hdrdir)/ruby/internal/stdalign.h capacity.o: $(hdrdir)/ruby/internal/stdbool.h +capacity.o: $(hdrdir)/ruby/internal/stdckdint.h capacity.o: $(hdrdir)/ruby/internal/symbol.h capacity.o: $(hdrdir)/ruby/internal/value.h capacity.o: $(hdrdir)/ruby/internal/value_type.h @@ -170,9 +172,170 @@ capacity.o: $(hdrdir)/ruby/oniguruma.h capacity.o: $(hdrdir)/ruby/ruby.h capacity.o: $(hdrdir)/ruby/st.h capacity.o: $(hdrdir)/ruby/subst.h +capacity.o: $(top_srcdir)/encindex.h capacity.o: $(top_srcdir)/internal/compilers.h capacity.o: $(top_srcdir)/internal/string.h capacity.o: capacity.c +chilled.o: $(RUBY_EXTCONF_H) +chilled.o: $(arch_hdrdir)/ruby/config.h +chilled.o: $(hdrdir)/ruby.h +chilled.o: $(hdrdir)/ruby/assert.h +chilled.o: $(hdrdir)/ruby/backward.h +chilled.o: $(hdrdir)/ruby/backward/2/assume.h +chilled.o: $(hdrdir)/ruby/backward/2/attributes.h +chilled.o: $(hdrdir)/ruby/backward/2/bool.h +chilled.o: $(hdrdir)/ruby/backward/2/inttypes.h +chilled.o: $(hdrdir)/ruby/backward/2/limits.h +chilled.o: $(hdrdir)/ruby/backward/2/long_long.h +chilled.o: $(hdrdir)/ruby/backward/2/stdalign.h +chilled.o: $(hdrdir)/ruby/backward/2/stdarg.h +chilled.o: $(hdrdir)/ruby/defines.h +chilled.o: $(hdrdir)/ruby/intern.h +chilled.o: $(hdrdir)/ruby/internal/abi.h +chilled.o: $(hdrdir)/ruby/internal/anyargs.h +chilled.o: $(hdrdir)/ruby/internal/arithmetic.h +chilled.o: $(hdrdir)/ruby/internal/arithmetic/char.h +chilled.o: $(hdrdir)/ruby/internal/arithmetic/double.h +chilled.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +chilled.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +chilled.o: $(hdrdir)/ruby/internal/arithmetic/int.h +chilled.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +chilled.o: $(hdrdir)/ruby/internal/arithmetic/long.h +chilled.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +chilled.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +chilled.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +chilled.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +chilled.o: $(hdrdir)/ruby/internal/arithmetic/short.h +chilled.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +chilled.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +chilled.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +chilled.o: $(hdrdir)/ruby/internal/assume.h +chilled.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +chilled.o: $(hdrdir)/ruby/internal/attr/artificial.h +chilled.o: $(hdrdir)/ruby/internal/attr/cold.h +chilled.o: $(hdrdir)/ruby/internal/attr/const.h +chilled.o: $(hdrdir)/ruby/internal/attr/constexpr.h +chilled.o: $(hdrdir)/ruby/internal/attr/deprecated.h +chilled.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +chilled.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +chilled.o: $(hdrdir)/ruby/internal/attr/error.h +chilled.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +chilled.o: $(hdrdir)/ruby/internal/attr/forceinline.h +chilled.o: $(hdrdir)/ruby/internal/attr/format.h +chilled.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +chilled.o: $(hdrdir)/ruby/internal/attr/noalias.h +chilled.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +chilled.o: $(hdrdir)/ruby/internal/attr/noexcept.h +chilled.o: $(hdrdir)/ruby/internal/attr/noinline.h +chilled.o: $(hdrdir)/ruby/internal/attr/nonnull.h +chilled.o: $(hdrdir)/ruby/internal/attr/noreturn.h +chilled.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +chilled.o: $(hdrdir)/ruby/internal/attr/pure.h +chilled.o: $(hdrdir)/ruby/internal/attr/restrict.h +chilled.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +chilled.o: $(hdrdir)/ruby/internal/attr/warning.h +chilled.o: $(hdrdir)/ruby/internal/attr/weakref.h +chilled.o: $(hdrdir)/ruby/internal/cast.h +chilled.o: $(hdrdir)/ruby/internal/compiler_is.h +chilled.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +chilled.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +chilled.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +chilled.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +chilled.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +chilled.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +chilled.o: $(hdrdir)/ruby/internal/compiler_since.h +chilled.o: $(hdrdir)/ruby/internal/config.h +chilled.o: $(hdrdir)/ruby/internal/constant_p.h +chilled.o: $(hdrdir)/ruby/internal/core.h +chilled.o: $(hdrdir)/ruby/internal/core/rarray.h +chilled.o: $(hdrdir)/ruby/internal/core/rbasic.h +chilled.o: $(hdrdir)/ruby/internal/core/rbignum.h +chilled.o: $(hdrdir)/ruby/internal/core/rclass.h +chilled.o: $(hdrdir)/ruby/internal/core/rdata.h +chilled.o: $(hdrdir)/ruby/internal/core/rfile.h +chilled.o: $(hdrdir)/ruby/internal/core/rhash.h +chilled.o: $(hdrdir)/ruby/internal/core/robject.h +chilled.o: $(hdrdir)/ruby/internal/core/rregexp.h +chilled.o: $(hdrdir)/ruby/internal/core/rstring.h +chilled.o: $(hdrdir)/ruby/internal/core/rstruct.h +chilled.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +chilled.o: $(hdrdir)/ruby/internal/ctype.h +chilled.o: $(hdrdir)/ruby/internal/dllexport.h +chilled.o: $(hdrdir)/ruby/internal/dosish.h +chilled.o: $(hdrdir)/ruby/internal/error.h +chilled.o: $(hdrdir)/ruby/internal/eval.h +chilled.o: $(hdrdir)/ruby/internal/event.h +chilled.o: $(hdrdir)/ruby/internal/fl_type.h +chilled.o: $(hdrdir)/ruby/internal/gc.h +chilled.o: $(hdrdir)/ruby/internal/glob.h +chilled.o: $(hdrdir)/ruby/internal/globals.h +chilled.o: $(hdrdir)/ruby/internal/has/attribute.h +chilled.o: $(hdrdir)/ruby/internal/has/builtin.h +chilled.o: $(hdrdir)/ruby/internal/has/c_attribute.h +chilled.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +chilled.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +chilled.o: $(hdrdir)/ruby/internal/has/extension.h +chilled.o: $(hdrdir)/ruby/internal/has/feature.h +chilled.o: $(hdrdir)/ruby/internal/has/warning.h +chilled.o: $(hdrdir)/ruby/internal/intern/array.h +chilled.o: $(hdrdir)/ruby/internal/intern/bignum.h +chilled.o: $(hdrdir)/ruby/internal/intern/class.h +chilled.o: $(hdrdir)/ruby/internal/intern/compar.h +chilled.o: $(hdrdir)/ruby/internal/intern/complex.h +chilled.o: $(hdrdir)/ruby/internal/intern/cont.h +chilled.o: $(hdrdir)/ruby/internal/intern/dir.h +chilled.o: $(hdrdir)/ruby/internal/intern/enum.h +chilled.o: $(hdrdir)/ruby/internal/intern/enumerator.h +chilled.o: $(hdrdir)/ruby/internal/intern/error.h +chilled.o: $(hdrdir)/ruby/internal/intern/eval.h +chilled.o: $(hdrdir)/ruby/internal/intern/file.h +chilled.o: $(hdrdir)/ruby/internal/intern/hash.h +chilled.o: $(hdrdir)/ruby/internal/intern/io.h +chilled.o: $(hdrdir)/ruby/internal/intern/load.h +chilled.o: $(hdrdir)/ruby/internal/intern/marshal.h +chilled.o: $(hdrdir)/ruby/internal/intern/numeric.h +chilled.o: $(hdrdir)/ruby/internal/intern/object.h +chilled.o: $(hdrdir)/ruby/internal/intern/parse.h +chilled.o: $(hdrdir)/ruby/internal/intern/proc.h +chilled.o: $(hdrdir)/ruby/internal/intern/process.h +chilled.o: $(hdrdir)/ruby/internal/intern/random.h +chilled.o: $(hdrdir)/ruby/internal/intern/range.h +chilled.o: $(hdrdir)/ruby/internal/intern/rational.h +chilled.o: $(hdrdir)/ruby/internal/intern/re.h +chilled.o: $(hdrdir)/ruby/internal/intern/ruby.h +chilled.o: $(hdrdir)/ruby/internal/intern/select.h +chilled.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +chilled.o: $(hdrdir)/ruby/internal/intern/signal.h +chilled.o: $(hdrdir)/ruby/internal/intern/sprintf.h +chilled.o: $(hdrdir)/ruby/internal/intern/string.h +chilled.o: $(hdrdir)/ruby/internal/intern/struct.h +chilled.o: $(hdrdir)/ruby/internal/intern/thread.h +chilled.o: $(hdrdir)/ruby/internal/intern/time.h +chilled.o: $(hdrdir)/ruby/internal/intern/variable.h +chilled.o: $(hdrdir)/ruby/internal/intern/vm.h +chilled.o: $(hdrdir)/ruby/internal/interpreter.h +chilled.o: $(hdrdir)/ruby/internal/iterator.h +chilled.o: $(hdrdir)/ruby/internal/memory.h +chilled.o: $(hdrdir)/ruby/internal/method.h +chilled.o: $(hdrdir)/ruby/internal/module.h +chilled.o: $(hdrdir)/ruby/internal/newobj.h +chilled.o: $(hdrdir)/ruby/internal/scan_args.h +chilled.o: $(hdrdir)/ruby/internal/special_consts.h +chilled.o: $(hdrdir)/ruby/internal/static_assert.h +chilled.o: $(hdrdir)/ruby/internal/stdalign.h +chilled.o: $(hdrdir)/ruby/internal/stdbool.h +chilled.o: $(hdrdir)/ruby/internal/stdckdint.h +chilled.o: $(hdrdir)/ruby/internal/symbol.h +chilled.o: $(hdrdir)/ruby/internal/value.h +chilled.o: $(hdrdir)/ruby/internal/value_type.h +chilled.o: $(hdrdir)/ruby/internal/variable.h +chilled.o: $(hdrdir)/ruby/internal/warning_push.h +chilled.o: $(hdrdir)/ruby/internal/xmalloc.h +chilled.o: $(hdrdir)/ruby/missing.h +chilled.o: $(hdrdir)/ruby/ruby.h +chilled.o: $(hdrdir)/ruby/st.h +chilled.o: $(hdrdir)/ruby/subst.h +chilled.o: chilled.c coderange.o: $(RUBY_EXTCONF_H) coderange.o: $(arch_hdrdir)/ruby/config.h coderange.o: $(hdrdir)/ruby/assert.h @@ -311,6 +474,7 @@ coderange.o: $(hdrdir)/ruby/internal/intern/re.h coderange.o: $(hdrdir)/ruby/internal/intern/ruby.h coderange.o: $(hdrdir)/ruby/internal/intern/select.h coderange.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +coderange.o: $(hdrdir)/ruby/internal/intern/set.h coderange.o: $(hdrdir)/ruby/internal/intern/signal.h coderange.o: $(hdrdir)/ruby/internal/intern/sprintf.h coderange.o: $(hdrdir)/ruby/internal/intern/string.h @@ -330,6 +494,7 @@ coderange.o: $(hdrdir)/ruby/internal/special_consts.h coderange.o: $(hdrdir)/ruby/internal/static_assert.h coderange.o: $(hdrdir)/ruby/internal/stdalign.h coderange.o: $(hdrdir)/ruby/internal/stdbool.h +coderange.o: $(hdrdir)/ruby/internal/stdckdint.h coderange.o: $(hdrdir)/ruby/internal/symbol.h coderange.o: $(hdrdir)/ruby/internal/value.h coderange.o: $(hdrdir)/ruby/internal/value_type.h @@ -482,6 +647,7 @@ cstr.o: $(hdrdir)/ruby/internal/intern/re.h cstr.o: $(hdrdir)/ruby/internal/intern/ruby.h cstr.o: $(hdrdir)/ruby/internal/intern/select.h cstr.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +cstr.o: $(hdrdir)/ruby/internal/intern/set.h cstr.o: $(hdrdir)/ruby/internal/intern/signal.h cstr.o: $(hdrdir)/ruby/internal/intern/sprintf.h cstr.o: $(hdrdir)/ruby/internal/intern/string.h @@ -501,6 +667,7 @@ cstr.o: $(hdrdir)/ruby/internal/special_consts.h cstr.o: $(hdrdir)/ruby/internal/static_assert.h cstr.o: $(hdrdir)/ruby/internal/stdalign.h cstr.o: $(hdrdir)/ruby/internal/stdbool.h +cstr.o: $(hdrdir)/ruby/internal/stdckdint.h cstr.o: $(hdrdir)/ruby/internal/symbol.h cstr.o: $(hdrdir)/ruby/internal/value.h cstr.o: $(hdrdir)/ruby/internal/value_type.h @@ -513,6 +680,7 @@ cstr.o: $(hdrdir)/ruby/oniguruma.h cstr.o: $(hdrdir)/ruby/ruby.h cstr.o: $(hdrdir)/ruby/st.h cstr.o: $(hdrdir)/ruby/subst.h +cstr.o: $(top_srcdir)/encindex.h cstr.o: $(top_srcdir)/internal.h cstr.o: $(top_srcdir)/internal/compilers.h cstr.o: $(top_srcdir)/internal/string.h @@ -646,6 +814,7 @@ ellipsize.o: $(hdrdir)/ruby/internal/intern/re.h ellipsize.o: $(hdrdir)/ruby/internal/intern/ruby.h ellipsize.o: $(hdrdir)/ruby/internal/intern/select.h ellipsize.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ellipsize.o: $(hdrdir)/ruby/internal/intern/set.h ellipsize.o: $(hdrdir)/ruby/internal/intern/signal.h ellipsize.o: $(hdrdir)/ruby/internal/intern/sprintf.h ellipsize.o: $(hdrdir)/ruby/internal/intern/string.h @@ -665,6 +834,7 @@ ellipsize.o: $(hdrdir)/ruby/internal/special_consts.h ellipsize.o: $(hdrdir)/ruby/internal/static_assert.h ellipsize.o: $(hdrdir)/ruby/internal/stdalign.h ellipsize.o: $(hdrdir)/ruby/internal/stdbool.h +ellipsize.o: $(hdrdir)/ruby/internal/stdckdint.h ellipsize.o: $(hdrdir)/ruby/internal/symbol.h ellipsize.o: $(hdrdir)/ruby/internal/value.h ellipsize.o: $(hdrdir)/ruby/internal/value_type.h @@ -815,6 +985,7 @@ enc_associate.o: $(hdrdir)/ruby/internal/intern/re.h enc_associate.o: $(hdrdir)/ruby/internal/intern/ruby.h enc_associate.o: $(hdrdir)/ruby/internal/intern/select.h enc_associate.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +enc_associate.o: $(hdrdir)/ruby/internal/intern/set.h enc_associate.o: $(hdrdir)/ruby/internal/intern/signal.h enc_associate.o: $(hdrdir)/ruby/internal/intern/sprintf.h enc_associate.o: $(hdrdir)/ruby/internal/intern/string.h @@ -834,6 +1005,7 @@ enc_associate.o: $(hdrdir)/ruby/internal/special_consts.h enc_associate.o: $(hdrdir)/ruby/internal/static_assert.h enc_associate.o: $(hdrdir)/ruby/internal/stdalign.h enc_associate.o: $(hdrdir)/ruby/internal/stdbool.h +enc_associate.o: $(hdrdir)/ruby/internal/stdckdint.h enc_associate.o: $(hdrdir)/ruby/internal/symbol.h enc_associate.o: $(hdrdir)/ruby/internal/value.h enc_associate.o: $(hdrdir)/ruby/internal/value_type.h @@ -986,6 +1158,7 @@ enc_dummy.o: $(hdrdir)/ruby/internal/intern/re.h enc_dummy.o: $(hdrdir)/ruby/internal/intern/ruby.h enc_dummy.o: $(hdrdir)/ruby/internal/intern/select.h enc_dummy.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +enc_dummy.o: $(hdrdir)/ruby/internal/intern/set.h enc_dummy.o: $(hdrdir)/ruby/internal/intern/signal.h enc_dummy.o: $(hdrdir)/ruby/internal/intern/sprintf.h enc_dummy.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1005,6 +1178,7 @@ enc_dummy.o: $(hdrdir)/ruby/internal/special_consts.h enc_dummy.o: $(hdrdir)/ruby/internal/static_assert.h enc_dummy.o: $(hdrdir)/ruby/internal/stdalign.h enc_dummy.o: $(hdrdir)/ruby/internal/stdbool.h +enc_dummy.o: $(hdrdir)/ruby/internal/stdckdint.h enc_dummy.o: $(hdrdir)/ruby/internal/symbol.h enc_dummy.o: $(hdrdir)/ruby/internal/value.h enc_dummy.o: $(hdrdir)/ruby/internal/value_type.h @@ -1156,6 +1330,7 @@ enc_str_buf_cat.o: $(hdrdir)/ruby/internal/intern/re.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/intern/ruby.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/intern/select.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +enc_str_buf_cat.o: $(hdrdir)/ruby/internal/intern/set.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/intern/signal.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/intern/sprintf.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1175,6 +1350,7 @@ enc_str_buf_cat.o: $(hdrdir)/ruby/internal/special_consts.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/static_assert.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/stdalign.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/stdbool.h +enc_str_buf_cat.o: $(hdrdir)/ruby/internal/stdckdint.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/symbol.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/value.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/value_type.h @@ -1328,6 +1504,7 @@ fstring.o: $(hdrdir)/ruby/internal/intern/re.h fstring.o: $(hdrdir)/ruby/internal/intern/ruby.h fstring.o: $(hdrdir)/ruby/internal/intern/select.h fstring.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +fstring.o: $(hdrdir)/ruby/internal/intern/set.h fstring.o: $(hdrdir)/ruby/internal/intern/signal.h fstring.o: $(hdrdir)/ruby/internal/intern/sprintf.h fstring.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1347,6 +1524,7 @@ fstring.o: $(hdrdir)/ruby/internal/special_consts.h fstring.o: $(hdrdir)/ruby/internal/static_assert.h fstring.o: $(hdrdir)/ruby/internal/stdalign.h fstring.o: $(hdrdir)/ruby/internal/stdbool.h +fstring.o: $(hdrdir)/ruby/internal/stdckdint.h fstring.o: $(hdrdir)/ruby/internal/symbol.h fstring.o: $(hdrdir)/ruby/internal/value.h fstring.o: $(hdrdir)/ruby/internal/value_type.h @@ -1359,6 +1537,7 @@ fstring.o: $(hdrdir)/ruby/oniguruma.h fstring.o: $(hdrdir)/ruby/ruby.h fstring.o: $(hdrdir)/ruby/st.h fstring.o: $(hdrdir)/ruby/subst.h +fstring.o: $(top_srcdir)/encindex.h fstring.o: $(top_srcdir)/internal/compilers.h fstring.o: $(top_srcdir)/internal/string.h fstring.o: fstring.c @@ -1491,6 +1670,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1510,6 +1690,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -1650,6 +1831,7 @@ modify.o: $(hdrdir)/ruby/internal/intern/re.h modify.o: $(hdrdir)/ruby/internal/intern/ruby.h modify.o: $(hdrdir)/ruby/internal/intern/select.h modify.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +modify.o: $(hdrdir)/ruby/internal/intern/set.h modify.o: $(hdrdir)/ruby/internal/intern/signal.h modify.o: $(hdrdir)/ruby/internal/intern/sprintf.h modify.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1669,6 +1851,7 @@ modify.o: $(hdrdir)/ruby/internal/special_consts.h modify.o: $(hdrdir)/ruby/internal/static_assert.h modify.o: $(hdrdir)/ruby/internal/stdalign.h modify.o: $(hdrdir)/ruby/internal/stdbool.h +modify.o: $(hdrdir)/ruby/internal/stdckdint.h modify.o: $(hdrdir)/ruby/internal/symbol.h modify.o: $(hdrdir)/ruby/internal/value.h modify.o: $(hdrdir)/ruby/internal/value_type.h @@ -1819,6 +2002,7 @@ new.o: $(hdrdir)/ruby/internal/intern/re.h new.o: $(hdrdir)/ruby/internal/intern/ruby.h new.o: $(hdrdir)/ruby/internal/intern/select.h new.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +new.o: $(hdrdir)/ruby/internal/intern/set.h new.o: $(hdrdir)/ruby/internal/intern/signal.h new.o: $(hdrdir)/ruby/internal/intern/sprintf.h new.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1838,6 +2022,7 @@ new.o: $(hdrdir)/ruby/internal/special_consts.h new.o: $(hdrdir)/ruby/internal/static_assert.h new.o: $(hdrdir)/ruby/internal/stdalign.h new.o: $(hdrdir)/ruby/internal/stdbool.h +new.o: $(hdrdir)/ruby/internal/stdckdint.h new.o: $(hdrdir)/ruby/internal/symbol.h new.o: $(hdrdir)/ruby/internal/value.h new.o: $(hdrdir)/ruby/internal/value_type.h @@ -1980,6 +2165,7 @@ nofree.o: $(hdrdir)/ruby/internal/intern/re.h nofree.o: $(hdrdir)/ruby/internal/intern/ruby.h nofree.o: $(hdrdir)/ruby/internal/intern/select.h nofree.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +nofree.o: $(hdrdir)/ruby/internal/intern/set.h nofree.o: $(hdrdir)/ruby/internal/intern/signal.h nofree.o: $(hdrdir)/ruby/internal/intern/sprintf.h nofree.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1999,6 +2185,7 @@ nofree.o: $(hdrdir)/ruby/internal/special_consts.h nofree.o: $(hdrdir)/ruby/internal/static_assert.h nofree.o: $(hdrdir)/ruby/internal/stdalign.h nofree.o: $(hdrdir)/ruby/internal/stdbool.h +nofree.o: $(hdrdir)/ruby/internal/stdckdint.h nofree.o: $(hdrdir)/ruby/internal/symbol.h nofree.o: $(hdrdir)/ruby/internal/value.h nofree.o: $(hdrdir)/ruby/internal/value_type.h @@ -2148,6 +2335,7 @@ normalize.o: $(hdrdir)/ruby/internal/intern/re.h normalize.o: $(hdrdir)/ruby/internal/intern/ruby.h normalize.o: $(hdrdir)/ruby/internal/intern/select.h normalize.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +normalize.o: $(hdrdir)/ruby/internal/intern/set.h normalize.o: $(hdrdir)/ruby/internal/intern/signal.h normalize.o: $(hdrdir)/ruby/internal/intern/sprintf.h normalize.o: $(hdrdir)/ruby/internal/intern/string.h @@ -2167,6 +2355,7 @@ normalize.o: $(hdrdir)/ruby/internal/special_consts.h normalize.o: $(hdrdir)/ruby/internal/static_assert.h normalize.o: $(hdrdir)/ruby/internal/stdalign.h normalize.o: $(hdrdir)/ruby/internal/stdbool.h +normalize.o: $(hdrdir)/ruby/internal/stdckdint.h normalize.o: $(hdrdir)/ruby/internal/symbol.h normalize.o: $(hdrdir)/ruby/internal/value.h normalize.o: $(hdrdir)/ruby/internal/value_type.h @@ -2320,6 +2509,7 @@ qsort.o: $(hdrdir)/ruby/internal/intern/re.h qsort.o: $(hdrdir)/ruby/internal/intern/ruby.h qsort.o: $(hdrdir)/ruby/internal/intern/select.h qsort.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +qsort.o: $(hdrdir)/ruby/internal/intern/set.h qsort.o: $(hdrdir)/ruby/internal/intern/signal.h qsort.o: $(hdrdir)/ruby/internal/intern/sprintf.h qsort.o: $(hdrdir)/ruby/internal/intern/string.h @@ -2339,6 +2529,7 @@ qsort.o: $(hdrdir)/ruby/internal/special_consts.h qsort.o: $(hdrdir)/ruby/internal/static_assert.h qsort.o: $(hdrdir)/ruby/internal/stdalign.h qsort.o: $(hdrdir)/ruby/internal/stdbool.h +qsort.o: $(hdrdir)/ruby/internal/stdckdint.h qsort.o: $(hdrdir)/ruby/internal/symbol.h qsort.o: $(hdrdir)/ruby/internal/value.h qsort.o: $(hdrdir)/ruby/internal/value_type.h @@ -2482,6 +2673,7 @@ rb_interned_str.o: $(hdrdir)/ruby/internal/intern/re.h rb_interned_str.o: $(hdrdir)/ruby/internal/intern/ruby.h rb_interned_str.o: $(hdrdir)/ruby/internal/intern/select.h rb_interned_str.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +rb_interned_str.o: $(hdrdir)/ruby/internal/intern/set.h rb_interned_str.o: $(hdrdir)/ruby/internal/intern/signal.h rb_interned_str.o: $(hdrdir)/ruby/internal/intern/sprintf.h rb_interned_str.o: $(hdrdir)/ruby/internal/intern/string.h @@ -2501,6 +2693,7 @@ rb_interned_str.o: $(hdrdir)/ruby/internal/special_consts.h rb_interned_str.o: $(hdrdir)/ruby/internal/static_assert.h rb_interned_str.o: $(hdrdir)/ruby/internal/stdalign.h rb_interned_str.o: $(hdrdir)/ruby/internal/stdbool.h +rb_interned_str.o: $(hdrdir)/ruby/internal/stdckdint.h rb_interned_str.o: $(hdrdir)/ruby/internal/symbol.h rb_interned_str.o: $(hdrdir)/ruby/internal/value.h rb_interned_str.o: $(hdrdir)/ruby/internal/value_type.h @@ -2641,6 +2834,7 @@ rb_str_dup.o: $(hdrdir)/ruby/internal/intern/re.h rb_str_dup.o: $(hdrdir)/ruby/internal/intern/ruby.h rb_str_dup.o: $(hdrdir)/ruby/internal/intern/select.h rb_str_dup.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +rb_str_dup.o: $(hdrdir)/ruby/internal/intern/set.h rb_str_dup.o: $(hdrdir)/ruby/internal/intern/signal.h rb_str_dup.o: $(hdrdir)/ruby/internal/intern/sprintf.h rb_str_dup.o: $(hdrdir)/ruby/internal/intern/string.h @@ -2660,6 +2854,7 @@ rb_str_dup.o: $(hdrdir)/ruby/internal/special_consts.h rb_str_dup.o: $(hdrdir)/ruby/internal/static_assert.h rb_str_dup.o: $(hdrdir)/ruby/internal/stdalign.h rb_str_dup.o: $(hdrdir)/ruby/internal/stdbool.h +rb_str_dup.o: $(hdrdir)/ruby/internal/stdckdint.h rb_str_dup.o: $(hdrdir)/ruby/internal/symbol.h rb_str_dup.o: $(hdrdir)/ruby/internal/value.h rb_str_dup.o: $(hdrdir)/ruby/internal/value_type.h @@ -2800,6 +2995,7 @@ set_len.o: $(hdrdir)/ruby/internal/intern/re.h set_len.o: $(hdrdir)/ruby/internal/intern/ruby.h set_len.o: $(hdrdir)/ruby/internal/intern/select.h set_len.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +set_len.o: $(hdrdir)/ruby/internal/intern/set.h set_len.o: $(hdrdir)/ruby/internal/intern/signal.h set_len.o: $(hdrdir)/ruby/internal/intern/sprintf.h set_len.o: $(hdrdir)/ruby/internal/intern/string.h @@ -2819,6 +3015,7 @@ set_len.o: $(hdrdir)/ruby/internal/special_consts.h set_len.o: $(hdrdir)/ruby/internal/static_assert.h set_len.o: $(hdrdir)/ruby/internal/stdalign.h set_len.o: $(hdrdir)/ruby/internal/stdbool.h +set_len.o: $(hdrdir)/ruby/internal/stdckdint.h set_len.o: $(hdrdir)/ruby/internal/symbol.h set_len.o: $(hdrdir)/ruby/internal/value.h set_len.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/string/fstring.c b/ext/-test-/string/fstring.c index 7ee14a8570..0b5940f28c 100644 --- a/ext/-test-/string/fstring.c +++ b/ext/-test-/string/fstring.c @@ -2,32 +2,30 @@ #include "ruby/encoding.h" #include "internal/string.h" -VALUE rb_fstring(VALUE str); - VALUE bug_s_fstring(VALUE self, VALUE str) { - return rb_fstring(str); + return rb_str_to_interned_str(str); } VALUE bug_s_fstring_fake_str(VALUE self) { static const char literal[] = "abcdefghijklmnopqrstuvwxyz"; - struct RString fake_str; - return rb_fstring(rb_setup_fake_str(&fake_str, literal, sizeof(literal) - 1, 0)); + struct RString fake_str = {RBASIC_INIT}; + return rb_str_to_interned_str(rb_setup_fake_str(&fake_str, literal, sizeof(literal) - 1, 0)); } VALUE bug_s_rb_enc_interned_str(VALUE self, VALUE encoding) { - return rb_enc_interned_str("foo", 3, RDATA(encoding)->data); + return rb_enc_interned_str("foo", 3, NIL_P(encoding) ? NULL : RTYPEDDATA_GET_DATA(encoding)); } VALUE bug_s_rb_enc_str_new(VALUE self, VALUE encoding) { - return rb_enc_str_new("foo", 3, RDATA(encoding)->data); + return rb_enc_str_new("foo", 3, NIL_P(encoding) ? NULL : RTYPEDDATA_GET_DATA(encoding)); } void diff --git a/ext/-test-/string/set_len.c b/ext/-test-/string/set_len.c index 049da2cdb5..b55ef6f469 100644 --- a/ext/-test-/string/set_len.c +++ b/ext/-test-/string/set_len.c @@ -16,9 +16,17 @@ bug_str_append(VALUE str, VALUE addendum) return str; } +static VALUE +bug_str_resize(VALUE str, VALUE len) +{ + rb_str_resize(str, NUM2LONG(len)); + return str; +} + void Init_string_set_len(VALUE klass) { rb_define_method(klass, "set_len", bug_str_set_len, 1); rb_define_method(klass, "append", bug_str_append, 1); + rb_define_method(klass, "resize", bug_str_resize, 1); } diff --git a/ext/-test-/struct/depend b/ext/-test-/struct/depend index 5db943e286..e2638e4cdf 100644 --- a/ext/-test-/struct/depend +++ b/ext/-test-/struct/depend @@ -128,6 +128,7 @@ data.o: $(hdrdir)/ruby/internal/intern/re.h data.o: $(hdrdir)/ruby/internal/intern/ruby.h data.o: $(hdrdir)/ruby/internal/intern/select.h data.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +data.o: $(hdrdir)/ruby/internal/intern/set.h data.o: $(hdrdir)/ruby/internal/intern/signal.h data.o: $(hdrdir)/ruby/internal/intern/sprintf.h data.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ data.o: $(hdrdir)/ruby/internal/special_consts.h data.o: $(hdrdir)/ruby/internal/static_assert.h data.o: $(hdrdir)/ruby/internal/stdalign.h data.o: $(hdrdir)/ruby/internal/stdbool.h +data.o: $(hdrdir)/ruby/internal/stdckdint.h data.o: $(hdrdir)/ruby/internal/symbol.h data.o: $(hdrdir)/ruby/internal/value.h data.o: $(hdrdir)/ruby/internal/value_type.h @@ -287,6 +289,7 @@ duplicate.o: $(hdrdir)/ruby/internal/intern/re.h duplicate.o: $(hdrdir)/ruby/internal/intern/ruby.h duplicate.o: $(hdrdir)/ruby/internal/intern/select.h duplicate.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +duplicate.o: $(hdrdir)/ruby/internal/intern/set.h duplicate.o: $(hdrdir)/ruby/internal/intern/signal.h duplicate.o: $(hdrdir)/ruby/internal/intern/sprintf.h duplicate.o: $(hdrdir)/ruby/internal/intern/string.h @@ -306,6 +309,7 @@ duplicate.o: $(hdrdir)/ruby/internal/special_consts.h duplicate.o: $(hdrdir)/ruby/internal/static_assert.h duplicate.o: $(hdrdir)/ruby/internal/stdalign.h duplicate.o: $(hdrdir)/ruby/internal/stdbool.h +duplicate.o: $(hdrdir)/ruby/internal/stdckdint.h duplicate.o: $(hdrdir)/ruby/internal/symbol.h duplicate.o: $(hdrdir)/ruby/internal/value.h duplicate.o: $(hdrdir)/ruby/internal/value_type.h @@ -446,6 +450,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -465,6 +470,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -605,6 +611,7 @@ len.o: $(hdrdir)/ruby/internal/intern/re.h len.o: $(hdrdir)/ruby/internal/intern/ruby.h len.o: $(hdrdir)/ruby/internal/intern/select.h len.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +len.o: $(hdrdir)/ruby/internal/intern/set.h len.o: $(hdrdir)/ruby/internal/intern/signal.h len.o: $(hdrdir)/ruby/internal/intern/sprintf.h len.o: $(hdrdir)/ruby/internal/intern/string.h @@ -624,6 +631,7 @@ len.o: $(hdrdir)/ruby/internal/special_consts.h len.o: $(hdrdir)/ruby/internal/static_assert.h len.o: $(hdrdir)/ruby/internal/stdalign.h len.o: $(hdrdir)/ruby/internal/stdbool.h +len.o: $(hdrdir)/ruby/internal/stdckdint.h len.o: $(hdrdir)/ruby/internal/symbol.h len.o: $(hdrdir)/ruby/internal/value.h len.o: $(hdrdir)/ruby/internal/value_type.h @@ -764,6 +772,7 @@ member.o: $(hdrdir)/ruby/internal/intern/re.h member.o: $(hdrdir)/ruby/internal/intern/ruby.h member.o: $(hdrdir)/ruby/internal/intern/select.h member.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +member.o: $(hdrdir)/ruby/internal/intern/set.h member.o: $(hdrdir)/ruby/internal/intern/signal.h member.o: $(hdrdir)/ruby/internal/intern/sprintf.h member.o: $(hdrdir)/ruby/internal/intern/string.h @@ -783,6 +792,7 @@ member.o: $(hdrdir)/ruby/internal/special_consts.h member.o: $(hdrdir)/ruby/internal/static_assert.h member.o: $(hdrdir)/ruby/internal/stdalign.h member.o: $(hdrdir)/ruby/internal/stdbool.h +member.o: $(hdrdir)/ruby/internal/stdckdint.h member.o: $(hdrdir)/ruby/internal/symbol.h member.o: $(hdrdir)/ruby/internal/value.h member.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/struct/member.c b/ext/-test-/struct/member.c index f5400fe477..29ddff93e8 100644 --- a/ext/-test-/struct/member.c +++ b/ext/-test-/struct/member.c @@ -6,7 +6,7 @@ bug_struct_get(VALUE obj, VALUE name) ID id = rb_check_id(&name); if (!id) { - rb_name_error_str(name, "`%"PRIsVALUE"' is not a struct member", name); + rb_name_error_str(name, "'%"PRIsVALUE"' is not a struct member", name); } return rb_struct_getmember(obj, id); } diff --git a/ext/-test-/symbol/depend b/ext/-test-/symbol/depend index dd1b5c305f..b1d8e1aab6 100644 --- a/ext/-test-/symbol/depend +++ b/ext/-test-/symbol/depend @@ -128,6 +128,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -287,6 +289,7 @@ type.o: $(hdrdir)/ruby/internal/intern/re.h type.o: $(hdrdir)/ruby/internal/intern/ruby.h type.o: $(hdrdir)/ruby/internal/intern/select.h type.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +type.o: $(hdrdir)/ruby/internal/intern/set.h type.o: $(hdrdir)/ruby/internal/intern/signal.h type.o: $(hdrdir)/ruby/internal/intern/sprintf.h type.o: $(hdrdir)/ruby/internal/intern/string.h @@ -306,6 +309,7 @@ type.o: $(hdrdir)/ruby/internal/special_consts.h type.o: $(hdrdir)/ruby/internal/static_assert.h type.o: $(hdrdir)/ruby/internal/stdalign.h type.o: $(hdrdir)/ruby/internal/stdbool.h +type.o: $(hdrdir)/ruby/internal/stdckdint.h type.o: $(hdrdir)/ruby/internal/symbol.h type.o: $(hdrdir)/ruby/internal/value.h type.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/thread/id/depend b/ext/-test-/thread/id/depend new file mode 100644 index 0000000000..6b76b31ddc --- /dev/null +++ b/ext/-test-/thread/id/depend @@ -0,0 +1,163 @@ +# AUTOGENERATED DEPENDENCIES START +id.o: $(RUBY_EXTCONF_H) +id.o: $(arch_hdrdir)/ruby/config.h +id.o: $(hdrdir)/ruby.h +id.o: $(hdrdir)/ruby/assert.h +id.o: $(hdrdir)/ruby/backward.h +id.o: $(hdrdir)/ruby/backward/2/assume.h +id.o: $(hdrdir)/ruby/backward/2/attributes.h +id.o: $(hdrdir)/ruby/backward/2/bool.h +id.o: $(hdrdir)/ruby/backward/2/inttypes.h +id.o: $(hdrdir)/ruby/backward/2/limits.h +id.o: $(hdrdir)/ruby/backward/2/long_long.h +id.o: $(hdrdir)/ruby/backward/2/stdalign.h +id.o: $(hdrdir)/ruby/backward/2/stdarg.h +id.o: $(hdrdir)/ruby/defines.h +id.o: $(hdrdir)/ruby/intern.h +id.o: $(hdrdir)/ruby/internal/abi.h +id.o: $(hdrdir)/ruby/internal/anyargs.h +id.o: $(hdrdir)/ruby/internal/arithmetic.h +id.o: $(hdrdir)/ruby/internal/arithmetic/char.h +id.o: $(hdrdir)/ruby/internal/arithmetic/double.h +id.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +id.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +id.o: $(hdrdir)/ruby/internal/arithmetic/int.h +id.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +id.o: $(hdrdir)/ruby/internal/arithmetic/long.h +id.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +id.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +id.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +id.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +id.o: $(hdrdir)/ruby/internal/arithmetic/short.h +id.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +id.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +id.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +id.o: $(hdrdir)/ruby/internal/assume.h +id.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +id.o: $(hdrdir)/ruby/internal/attr/artificial.h +id.o: $(hdrdir)/ruby/internal/attr/cold.h +id.o: $(hdrdir)/ruby/internal/attr/const.h +id.o: $(hdrdir)/ruby/internal/attr/constexpr.h +id.o: $(hdrdir)/ruby/internal/attr/deprecated.h +id.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +id.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +id.o: $(hdrdir)/ruby/internal/attr/error.h +id.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +id.o: $(hdrdir)/ruby/internal/attr/forceinline.h +id.o: $(hdrdir)/ruby/internal/attr/format.h +id.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +id.o: $(hdrdir)/ruby/internal/attr/noalias.h +id.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +id.o: $(hdrdir)/ruby/internal/attr/noexcept.h +id.o: $(hdrdir)/ruby/internal/attr/noinline.h +id.o: $(hdrdir)/ruby/internal/attr/nonnull.h +id.o: $(hdrdir)/ruby/internal/attr/noreturn.h +id.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +id.o: $(hdrdir)/ruby/internal/attr/pure.h +id.o: $(hdrdir)/ruby/internal/attr/restrict.h +id.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +id.o: $(hdrdir)/ruby/internal/attr/warning.h +id.o: $(hdrdir)/ruby/internal/attr/weakref.h +id.o: $(hdrdir)/ruby/internal/cast.h +id.o: $(hdrdir)/ruby/internal/compiler_is.h +id.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +id.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +id.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +id.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +id.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +id.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +id.o: $(hdrdir)/ruby/internal/compiler_since.h +id.o: $(hdrdir)/ruby/internal/config.h +id.o: $(hdrdir)/ruby/internal/constant_p.h +id.o: $(hdrdir)/ruby/internal/core.h +id.o: $(hdrdir)/ruby/internal/core/rarray.h +id.o: $(hdrdir)/ruby/internal/core/rbasic.h +id.o: $(hdrdir)/ruby/internal/core/rbignum.h +id.o: $(hdrdir)/ruby/internal/core/rclass.h +id.o: $(hdrdir)/ruby/internal/core/rdata.h +id.o: $(hdrdir)/ruby/internal/core/rfile.h +id.o: $(hdrdir)/ruby/internal/core/rhash.h +id.o: $(hdrdir)/ruby/internal/core/robject.h +id.o: $(hdrdir)/ruby/internal/core/rregexp.h +id.o: $(hdrdir)/ruby/internal/core/rstring.h +id.o: $(hdrdir)/ruby/internal/core/rstruct.h +id.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +id.o: $(hdrdir)/ruby/internal/ctype.h +id.o: $(hdrdir)/ruby/internal/dllexport.h +id.o: $(hdrdir)/ruby/internal/dosish.h +id.o: $(hdrdir)/ruby/internal/error.h +id.o: $(hdrdir)/ruby/internal/eval.h +id.o: $(hdrdir)/ruby/internal/event.h +id.o: $(hdrdir)/ruby/internal/fl_type.h +id.o: $(hdrdir)/ruby/internal/gc.h +id.o: $(hdrdir)/ruby/internal/glob.h +id.o: $(hdrdir)/ruby/internal/globals.h +id.o: $(hdrdir)/ruby/internal/has/attribute.h +id.o: $(hdrdir)/ruby/internal/has/builtin.h +id.o: $(hdrdir)/ruby/internal/has/c_attribute.h +id.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +id.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +id.o: $(hdrdir)/ruby/internal/has/extension.h +id.o: $(hdrdir)/ruby/internal/has/feature.h +id.o: $(hdrdir)/ruby/internal/has/warning.h +id.o: $(hdrdir)/ruby/internal/intern/array.h +id.o: $(hdrdir)/ruby/internal/intern/bignum.h +id.o: $(hdrdir)/ruby/internal/intern/class.h +id.o: $(hdrdir)/ruby/internal/intern/compar.h +id.o: $(hdrdir)/ruby/internal/intern/complex.h +id.o: $(hdrdir)/ruby/internal/intern/cont.h +id.o: $(hdrdir)/ruby/internal/intern/dir.h +id.o: $(hdrdir)/ruby/internal/intern/enum.h +id.o: $(hdrdir)/ruby/internal/intern/enumerator.h +id.o: $(hdrdir)/ruby/internal/intern/error.h +id.o: $(hdrdir)/ruby/internal/intern/eval.h +id.o: $(hdrdir)/ruby/internal/intern/file.h +id.o: $(hdrdir)/ruby/internal/intern/hash.h +id.o: $(hdrdir)/ruby/internal/intern/io.h +id.o: $(hdrdir)/ruby/internal/intern/load.h +id.o: $(hdrdir)/ruby/internal/intern/marshal.h +id.o: $(hdrdir)/ruby/internal/intern/numeric.h +id.o: $(hdrdir)/ruby/internal/intern/object.h +id.o: $(hdrdir)/ruby/internal/intern/parse.h +id.o: $(hdrdir)/ruby/internal/intern/proc.h +id.o: $(hdrdir)/ruby/internal/intern/process.h +id.o: $(hdrdir)/ruby/internal/intern/random.h +id.o: $(hdrdir)/ruby/internal/intern/range.h +id.o: $(hdrdir)/ruby/internal/intern/rational.h +id.o: $(hdrdir)/ruby/internal/intern/re.h +id.o: $(hdrdir)/ruby/internal/intern/ruby.h +id.o: $(hdrdir)/ruby/internal/intern/select.h +id.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +id.o: $(hdrdir)/ruby/internal/intern/set.h +id.o: $(hdrdir)/ruby/internal/intern/signal.h +id.o: $(hdrdir)/ruby/internal/intern/sprintf.h +id.o: $(hdrdir)/ruby/internal/intern/string.h +id.o: $(hdrdir)/ruby/internal/intern/struct.h +id.o: $(hdrdir)/ruby/internal/intern/thread.h +id.o: $(hdrdir)/ruby/internal/intern/time.h +id.o: $(hdrdir)/ruby/internal/intern/variable.h +id.o: $(hdrdir)/ruby/internal/intern/vm.h +id.o: $(hdrdir)/ruby/internal/interpreter.h +id.o: $(hdrdir)/ruby/internal/iterator.h +id.o: $(hdrdir)/ruby/internal/memory.h +id.o: $(hdrdir)/ruby/internal/method.h +id.o: $(hdrdir)/ruby/internal/module.h +id.o: $(hdrdir)/ruby/internal/newobj.h +id.o: $(hdrdir)/ruby/internal/scan_args.h +id.o: $(hdrdir)/ruby/internal/special_consts.h +id.o: $(hdrdir)/ruby/internal/static_assert.h +id.o: $(hdrdir)/ruby/internal/stdalign.h +id.o: $(hdrdir)/ruby/internal/stdbool.h +id.o: $(hdrdir)/ruby/internal/stdckdint.h +id.o: $(hdrdir)/ruby/internal/symbol.h +id.o: $(hdrdir)/ruby/internal/value.h +id.o: $(hdrdir)/ruby/internal/value_type.h +id.o: $(hdrdir)/ruby/internal/variable.h +id.o: $(hdrdir)/ruby/internal/warning_push.h +id.o: $(hdrdir)/ruby/internal/xmalloc.h +id.o: $(hdrdir)/ruby/missing.h +id.o: $(hdrdir)/ruby/ruby.h +id.o: $(hdrdir)/ruby/st.h +id.o: $(hdrdir)/ruby/subst.h +id.o: id.c +# AUTOGENERATED DEPENDENCIES END diff --git a/ext/-test-/thread/id/extconf.rb b/ext/-test-/thread/id/extconf.rb new file mode 100644 index 0000000000..a0ae0eff15 --- /dev/null +++ b/ext/-test-/thread/id/extconf.rb @@ -0,0 +1,3 @@ +if have_func("gettid") + create_makefile("-test-/thread/id") +end diff --git a/ext/-test-/thread/id/id.c b/ext/-test-/thread/id/id.c new file mode 100644 index 0000000000..b46a5955e2 --- /dev/null +++ b/ext/-test-/thread/id/id.c @@ -0,0 +1,15 @@ +#include <ruby.h> + +static VALUE +bug_gettid(VALUE self) +{ + pid_t tid = gettid(); + return PIDT2NUM(tid); +} + +void +Init_id(void) +{ + VALUE klass = rb_define_module_under(rb_define_module("Bug"), "ThreadID"); + rb_define_module_function(klass, "gettid", bug_gettid, 0); +} diff --git a/ext/-test-/thread/instrumentation/depend b/ext/-test-/thread/instrumentation/depend index b03f51870f..63e1c7e44f 100644 --- a/ext/-test-/thread/instrumentation/depend +++ b/ext/-test-/thread/instrumentation/depend @@ -128,6 +128,7 @@ instrumentation.o: $(hdrdir)/ruby/internal/intern/re.h instrumentation.o: $(hdrdir)/ruby/internal/intern/ruby.h instrumentation.o: $(hdrdir)/ruby/internal/intern/select.h instrumentation.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +instrumentation.o: $(hdrdir)/ruby/internal/intern/set.h instrumentation.o: $(hdrdir)/ruby/internal/intern/signal.h instrumentation.o: $(hdrdir)/ruby/internal/intern/sprintf.h instrumentation.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ instrumentation.o: $(hdrdir)/ruby/internal/special_consts.h instrumentation.o: $(hdrdir)/ruby/internal/static_assert.h instrumentation.o: $(hdrdir)/ruby/internal/stdalign.h instrumentation.o: $(hdrdir)/ruby/internal/stdbool.h +instrumentation.o: $(hdrdir)/ruby/internal/stdckdint.h instrumentation.o: $(hdrdir)/ruby/internal/symbol.h instrumentation.o: $(hdrdir)/ruby/internal/value.h instrumentation.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/thread/instrumentation/instrumentation.c b/ext/-test-/thread/instrumentation/instrumentation.c index d81bc0f2a7..25e2902a78 100644 --- a/ext/-test-/thread/instrumentation/instrumentation.c +++ b/ext/-test-/thread/instrumentation/instrumentation.c @@ -6,7 +6,6 @@ # define RB_THREAD_LOCAL_SPECIFIER #endif -static VALUE last_thread = Qnil; static VALUE timeline_value = Qnil; struct thread_event { @@ -211,9 +210,8 @@ Init_instrumentation(void) VALUE mBug = rb_define_module("Bug"); VALUE klass = rb_define_module_under(mBug, "ThreadInstrumentation"); rb_global_variable(&timeline_value); - timeline_value = TypedData_Wrap_Struct(0, &event_timeline_type, 0); + timeline_value = TypedData_Wrap_Struct(0, &event_timeline_type, (void *)1); - rb_global_variable(&last_thread); rb_define_singleton_method(klass, "register_callback", thread_register_callback, 1); rb_define_singleton_method(klass, "unregister_callback", thread_unregister_callback, 0); rb_define_singleton_method(klass, "register_and_unregister_callbacks", thread_register_and_unregister_callback, 0); diff --git a/ext/-test-/thread/lock_native_thread/depend b/ext/-test-/thread/lock_native_thread/depend new file mode 100644 index 0000000000..a32843e531 --- /dev/null +++ b/ext/-test-/thread/lock_native_thread/depend @@ -0,0 +1,163 @@ +# AUTOGENERATED DEPENDENCIES START +lock_native_thread.o: $(RUBY_EXTCONF_H) +lock_native_thread.o: $(arch_hdrdir)/ruby/config.h +lock_native_thread.o: $(hdrdir)/ruby/assert.h +lock_native_thread.o: $(hdrdir)/ruby/backward.h +lock_native_thread.o: $(hdrdir)/ruby/backward/2/assume.h +lock_native_thread.o: $(hdrdir)/ruby/backward/2/attributes.h +lock_native_thread.o: $(hdrdir)/ruby/backward/2/bool.h +lock_native_thread.o: $(hdrdir)/ruby/backward/2/inttypes.h +lock_native_thread.o: $(hdrdir)/ruby/backward/2/limits.h +lock_native_thread.o: $(hdrdir)/ruby/backward/2/long_long.h +lock_native_thread.o: $(hdrdir)/ruby/backward/2/stdalign.h +lock_native_thread.o: $(hdrdir)/ruby/backward/2/stdarg.h +lock_native_thread.o: $(hdrdir)/ruby/defines.h +lock_native_thread.o: $(hdrdir)/ruby/intern.h +lock_native_thread.o: $(hdrdir)/ruby/internal/abi.h +lock_native_thread.o: $(hdrdir)/ruby/internal/anyargs.h +lock_native_thread.o: $(hdrdir)/ruby/internal/arithmetic.h +lock_native_thread.o: $(hdrdir)/ruby/internal/arithmetic/char.h +lock_native_thread.o: $(hdrdir)/ruby/internal/arithmetic/double.h +lock_native_thread.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +lock_native_thread.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +lock_native_thread.o: $(hdrdir)/ruby/internal/arithmetic/int.h +lock_native_thread.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +lock_native_thread.o: $(hdrdir)/ruby/internal/arithmetic/long.h +lock_native_thread.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +lock_native_thread.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +lock_native_thread.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +lock_native_thread.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +lock_native_thread.o: $(hdrdir)/ruby/internal/arithmetic/short.h +lock_native_thread.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +lock_native_thread.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +lock_native_thread.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +lock_native_thread.o: $(hdrdir)/ruby/internal/assume.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/artificial.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/cold.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/const.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/constexpr.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/deprecated.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/error.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/forceinline.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/format.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/noalias.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/noexcept.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/noinline.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/nonnull.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/noreturn.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/pure.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/restrict.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/warning.h +lock_native_thread.o: $(hdrdir)/ruby/internal/attr/weakref.h +lock_native_thread.o: $(hdrdir)/ruby/internal/cast.h +lock_native_thread.o: $(hdrdir)/ruby/internal/compiler_is.h +lock_native_thread.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +lock_native_thread.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +lock_native_thread.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +lock_native_thread.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +lock_native_thread.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +lock_native_thread.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +lock_native_thread.o: $(hdrdir)/ruby/internal/compiler_since.h +lock_native_thread.o: $(hdrdir)/ruby/internal/config.h +lock_native_thread.o: $(hdrdir)/ruby/internal/constant_p.h +lock_native_thread.o: $(hdrdir)/ruby/internal/core.h +lock_native_thread.o: $(hdrdir)/ruby/internal/core/rarray.h +lock_native_thread.o: $(hdrdir)/ruby/internal/core/rbasic.h +lock_native_thread.o: $(hdrdir)/ruby/internal/core/rbignum.h +lock_native_thread.o: $(hdrdir)/ruby/internal/core/rclass.h +lock_native_thread.o: $(hdrdir)/ruby/internal/core/rdata.h +lock_native_thread.o: $(hdrdir)/ruby/internal/core/rfile.h +lock_native_thread.o: $(hdrdir)/ruby/internal/core/rhash.h +lock_native_thread.o: $(hdrdir)/ruby/internal/core/robject.h +lock_native_thread.o: $(hdrdir)/ruby/internal/core/rregexp.h +lock_native_thread.o: $(hdrdir)/ruby/internal/core/rstring.h +lock_native_thread.o: $(hdrdir)/ruby/internal/core/rstruct.h +lock_native_thread.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +lock_native_thread.o: $(hdrdir)/ruby/internal/ctype.h +lock_native_thread.o: $(hdrdir)/ruby/internal/dllexport.h +lock_native_thread.o: $(hdrdir)/ruby/internal/dosish.h +lock_native_thread.o: $(hdrdir)/ruby/internal/error.h +lock_native_thread.o: $(hdrdir)/ruby/internal/eval.h +lock_native_thread.o: $(hdrdir)/ruby/internal/event.h +lock_native_thread.o: $(hdrdir)/ruby/internal/fl_type.h +lock_native_thread.o: $(hdrdir)/ruby/internal/gc.h +lock_native_thread.o: $(hdrdir)/ruby/internal/glob.h +lock_native_thread.o: $(hdrdir)/ruby/internal/globals.h +lock_native_thread.o: $(hdrdir)/ruby/internal/has/attribute.h +lock_native_thread.o: $(hdrdir)/ruby/internal/has/builtin.h +lock_native_thread.o: $(hdrdir)/ruby/internal/has/c_attribute.h +lock_native_thread.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +lock_native_thread.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +lock_native_thread.o: $(hdrdir)/ruby/internal/has/extension.h +lock_native_thread.o: $(hdrdir)/ruby/internal/has/feature.h +lock_native_thread.o: $(hdrdir)/ruby/internal/has/warning.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/array.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/bignum.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/class.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/compar.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/complex.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/cont.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/dir.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/enum.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/enumerator.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/error.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/eval.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/file.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/hash.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/io.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/load.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/marshal.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/numeric.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/object.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/parse.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/proc.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/process.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/random.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/range.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/rational.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/re.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/ruby.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/select.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/set.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/signal.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/sprintf.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/string.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/struct.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/thread.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/time.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/variable.h +lock_native_thread.o: $(hdrdir)/ruby/internal/intern/vm.h +lock_native_thread.o: $(hdrdir)/ruby/internal/interpreter.h +lock_native_thread.o: $(hdrdir)/ruby/internal/iterator.h +lock_native_thread.o: $(hdrdir)/ruby/internal/memory.h +lock_native_thread.o: $(hdrdir)/ruby/internal/method.h +lock_native_thread.o: $(hdrdir)/ruby/internal/module.h +lock_native_thread.o: $(hdrdir)/ruby/internal/newobj.h +lock_native_thread.o: $(hdrdir)/ruby/internal/scan_args.h +lock_native_thread.o: $(hdrdir)/ruby/internal/special_consts.h +lock_native_thread.o: $(hdrdir)/ruby/internal/static_assert.h +lock_native_thread.o: $(hdrdir)/ruby/internal/stdalign.h +lock_native_thread.o: $(hdrdir)/ruby/internal/stdbool.h +lock_native_thread.o: $(hdrdir)/ruby/internal/stdckdint.h +lock_native_thread.o: $(hdrdir)/ruby/internal/symbol.h +lock_native_thread.o: $(hdrdir)/ruby/internal/value.h +lock_native_thread.o: $(hdrdir)/ruby/internal/value_type.h +lock_native_thread.o: $(hdrdir)/ruby/internal/variable.h +lock_native_thread.o: $(hdrdir)/ruby/internal/warning_push.h +lock_native_thread.o: $(hdrdir)/ruby/internal/xmalloc.h +lock_native_thread.o: $(hdrdir)/ruby/missing.h +lock_native_thread.o: $(hdrdir)/ruby/ruby.h +lock_native_thread.o: $(hdrdir)/ruby/st.h +lock_native_thread.o: $(hdrdir)/ruby/subst.h +lock_native_thread.o: $(hdrdir)/ruby/thread.h +lock_native_thread.o: lock_native_thread.c +# AUTOGENERATED DEPENDENCIES END diff --git a/ext/-test-/thread/lock_native_thread/extconf.rb b/ext/-test-/thread/lock_native_thread/extconf.rb new file mode 100644 index 0000000000..832bfde01a --- /dev/null +++ b/ext/-test-/thread/lock_native_thread/extconf.rb @@ -0,0 +1,2 @@ +# frozen_string_literal: false +create_makefile("-test-/thread/lock_native_thread") diff --git a/ext/-test-/thread/lock_native_thread/lock_native_thread.c b/ext/-test-/thread/lock_native_thread/lock_native_thread.c new file mode 100644 index 0000000000..2eb75809a9 --- /dev/null +++ b/ext/-test-/thread/lock_native_thread/lock_native_thread.c @@ -0,0 +1,50 @@ + +#include "ruby/ruby.h" +#include "ruby/thread.h" + +#ifdef HAVE_PTHREAD_H +#include <pthread.h> + +static pthread_key_t tls_key; + +static VALUE +get_tls(VALUE self) +{ + return (VALUE)pthread_getspecific(tls_key); +} + +static VALUE +set_tls(VALUE self, VALUE vn) +{ + pthread_setspecific(tls_key, (void *)vn); + return Qnil; +} + +static VALUE +lock_native_thread(VALUE self) +{ + return rb_thread_lock_native_thread() ? Qtrue : Qfalse; +} + +void +Init_lock_native_thread(void) +{ + int r; + + if ((r = pthread_key_create(&tls_key, NULL)) != 0) { + rb_bug("pthread_key_create() returns %d", r); + } + pthread_setspecific(tls_key, NULL); + + rb_define_method(rb_cThread, "lock_native_thread", lock_native_thread, 0); + rb_define_method(rb_cThread, "get_tls", get_tls, 0); + rb_define_method(rb_cThread, "set_tls", set_tls, 1); +} + +#else // HAVE_PTHREAD_H +void +Init_lock_native_thread(void) +{ + // do nothing +} +#endif // HAVE_PTHREAD_H diff --git a/ext/-test-/thread_fd/depend b/ext/-test-/thread_fd/depend deleted file mode 100644 index d4cc772526..0000000000 --- a/ext/-test-/thread_fd/depend +++ /dev/null @@ -1,160 +0,0 @@ -# AUTOGENERATED DEPENDENCIES START -thread_fd.o: $(RUBY_EXTCONF_H) -thread_fd.o: $(arch_hdrdir)/ruby/config.h -thread_fd.o: $(hdrdir)/ruby/assert.h -thread_fd.o: $(hdrdir)/ruby/backward.h -thread_fd.o: $(hdrdir)/ruby/backward/2/assume.h -thread_fd.o: $(hdrdir)/ruby/backward/2/attributes.h -thread_fd.o: $(hdrdir)/ruby/backward/2/bool.h -thread_fd.o: $(hdrdir)/ruby/backward/2/inttypes.h -thread_fd.o: $(hdrdir)/ruby/backward/2/limits.h -thread_fd.o: $(hdrdir)/ruby/backward/2/long_long.h -thread_fd.o: $(hdrdir)/ruby/backward/2/stdalign.h -thread_fd.o: $(hdrdir)/ruby/backward/2/stdarg.h -thread_fd.o: $(hdrdir)/ruby/defines.h -thread_fd.o: $(hdrdir)/ruby/intern.h -thread_fd.o: $(hdrdir)/ruby/internal/abi.h -thread_fd.o: $(hdrdir)/ruby/internal/anyargs.h -thread_fd.o: $(hdrdir)/ruby/internal/arithmetic.h -thread_fd.o: $(hdrdir)/ruby/internal/arithmetic/char.h -thread_fd.o: $(hdrdir)/ruby/internal/arithmetic/double.h -thread_fd.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h -thread_fd.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h -thread_fd.o: $(hdrdir)/ruby/internal/arithmetic/int.h -thread_fd.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h -thread_fd.o: $(hdrdir)/ruby/internal/arithmetic/long.h -thread_fd.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h -thread_fd.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h -thread_fd.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h -thread_fd.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h -thread_fd.o: $(hdrdir)/ruby/internal/arithmetic/short.h -thread_fd.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h -thread_fd.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h -thread_fd.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h -thread_fd.o: $(hdrdir)/ruby/internal/assume.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/alloc_size.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/artificial.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/cold.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/const.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/constexpr.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/deprecated.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/error.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/flag_enum.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/forceinline.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/format.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/noalias.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/nodiscard.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/noexcept.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/noinline.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/nonnull.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/noreturn.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/packed_struct.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/pure.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/restrict.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/warning.h -thread_fd.o: $(hdrdir)/ruby/internal/attr/weakref.h -thread_fd.o: $(hdrdir)/ruby/internal/cast.h -thread_fd.o: $(hdrdir)/ruby/internal/compiler_is.h -thread_fd.o: $(hdrdir)/ruby/internal/compiler_is/apple.h -thread_fd.o: $(hdrdir)/ruby/internal/compiler_is/clang.h -thread_fd.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h -thread_fd.o: $(hdrdir)/ruby/internal/compiler_is/intel.h -thread_fd.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h -thread_fd.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h -thread_fd.o: $(hdrdir)/ruby/internal/compiler_since.h -thread_fd.o: $(hdrdir)/ruby/internal/config.h -thread_fd.o: $(hdrdir)/ruby/internal/constant_p.h -thread_fd.o: $(hdrdir)/ruby/internal/core.h -thread_fd.o: $(hdrdir)/ruby/internal/core/rarray.h -thread_fd.o: $(hdrdir)/ruby/internal/core/rbasic.h -thread_fd.o: $(hdrdir)/ruby/internal/core/rbignum.h -thread_fd.o: $(hdrdir)/ruby/internal/core/rclass.h -thread_fd.o: $(hdrdir)/ruby/internal/core/rdata.h -thread_fd.o: $(hdrdir)/ruby/internal/core/rfile.h -thread_fd.o: $(hdrdir)/ruby/internal/core/rhash.h -thread_fd.o: $(hdrdir)/ruby/internal/core/robject.h -thread_fd.o: $(hdrdir)/ruby/internal/core/rregexp.h -thread_fd.o: $(hdrdir)/ruby/internal/core/rstring.h -thread_fd.o: $(hdrdir)/ruby/internal/core/rstruct.h -thread_fd.o: $(hdrdir)/ruby/internal/core/rtypeddata.h -thread_fd.o: $(hdrdir)/ruby/internal/ctype.h -thread_fd.o: $(hdrdir)/ruby/internal/dllexport.h -thread_fd.o: $(hdrdir)/ruby/internal/dosish.h -thread_fd.o: $(hdrdir)/ruby/internal/error.h -thread_fd.o: $(hdrdir)/ruby/internal/eval.h -thread_fd.o: $(hdrdir)/ruby/internal/event.h -thread_fd.o: $(hdrdir)/ruby/internal/fl_type.h -thread_fd.o: $(hdrdir)/ruby/internal/gc.h -thread_fd.o: $(hdrdir)/ruby/internal/glob.h -thread_fd.o: $(hdrdir)/ruby/internal/globals.h -thread_fd.o: $(hdrdir)/ruby/internal/has/attribute.h -thread_fd.o: $(hdrdir)/ruby/internal/has/builtin.h -thread_fd.o: $(hdrdir)/ruby/internal/has/c_attribute.h -thread_fd.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h -thread_fd.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h -thread_fd.o: $(hdrdir)/ruby/internal/has/extension.h -thread_fd.o: $(hdrdir)/ruby/internal/has/feature.h -thread_fd.o: $(hdrdir)/ruby/internal/has/warning.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/array.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/bignum.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/class.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/compar.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/complex.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/cont.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/dir.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/enum.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/enumerator.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/error.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/eval.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/file.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/hash.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/io.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/load.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/marshal.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/numeric.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/object.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/parse.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/proc.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/process.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/random.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/range.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/rational.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/re.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/ruby.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/select.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/select/largesize.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/signal.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/sprintf.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/string.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/struct.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/thread.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/time.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/variable.h -thread_fd.o: $(hdrdir)/ruby/internal/intern/vm.h -thread_fd.o: $(hdrdir)/ruby/internal/interpreter.h -thread_fd.o: $(hdrdir)/ruby/internal/iterator.h -thread_fd.o: $(hdrdir)/ruby/internal/memory.h -thread_fd.o: $(hdrdir)/ruby/internal/method.h -thread_fd.o: $(hdrdir)/ruby/internal/module.h -thread_fd.o: $(hdrdir)/ruby/internal/newobj.h -thread_fd.o: $(hdrdir)/ruby/internal/scan_args.h -thread_fd.o: $(hdrdir)/ruby/internal/special_consts.h -thread_fd.o: $(hdrdir)/ruby/internal/static_assert.h -thread_fd.o: $(hdrdir)/ruby/internal/stdalign.h -thread_fd.o: $(hdrdir)/ruby/internal/stdbool.h -thread_fd.o: $(hdrdir)/ruby/internal/symbol.h -thread_fd.o: $(hdrdir)/ruby/internal/value.h -thread_fd.o: $(hdrdir)/ruby/internal/value_type.h -thread_fd.o: $(hdrdir)/ruby/internal/variable.h -thread_fd.o: $(hdrdir)/ruby/internal/warning_push.h -thread_fd.o: $(hdrdir)/ruby/internal/xmalloc.h -thread_fd.o: $(hdrdir)/ruby/missing.h -thread_fd.o: $(hdrdir)/ruby/ruby.h -thread_fd.o: $(hdrdir)/ruby/st.h -thread_fd.o: $(hdrdir)/ruby/subst.h -thread_fd.o: thread_fd.c -# AUTOGENERATED DEPENDENCIES END diff --git a/ext/-test-/thread_fd/extconf.rb b/ext/-test-/thread_fd/extconf.rb deleted file mode 100644 index a8bbe9d169..0000000000 --- a/ext/-test-/thread_fd/extconf.rb +++ /dev/null @@ -1,2 +0,0 @@ -# frozen_string_literal: true -create_makefile('-test-/thread_fd') diff --git a/ext/-test-/thread_fd/thread_fd.c b/ext/-test-/thread_fd/thread_fd.c deleted file mode 100644 index 042b799dc8..0000000000 --- a/ext/-test-/thread_fd/thread_fd.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "ruby/ruby.h" - -static VALUE -thread_fd_close(VALUE ign, VALUE fd) -{ - rb_thread_fd_close(NUM2INT(fd)); - return Qnil; -} - -static VALUE -thread_fd_wait(VALUE ign, VALUE fd) -{ - int ret = rb_thread_wait_fd(NUM2INT(fd)); - return INT2NUM(ret); -} - -static VALUE -thread_fd_writable(VALUE ign, VALUE fd) -{ - int ret = rb_thread_fd_writable(NUM2INT(fd)); - return INT2NUM(ret); -} - -void -Init_thread_fd(void) -{ - rb_define_singleton_method(rb_cIO, "thread_fd_close", thread_fd_close, 1); - rb_define_singleton_method(rb_cIO, "thread_fd_wait", thread_fd_wait, 1); - rb_define_singleton_method(rb_cIO, "thread_fd_writable", thread_fd_writable, 1); -} diff --git a/ext/-test-/time/depend b/ext/-test-/time/depend index c015588b09..e5b05f3113 100644 --- a/ext/-test-/time/depend +++ b/ext/-test-/time/depend @@ -128,6 +128,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -288,6 +290,7 @@ leap_second.o: $(hdrdir)/ruby/internal/intern/re.h leap_second.o: $(hdrdir)/ruby/internal/intern/ruby.h leap_second.o: $(hdrdir)/ruby/internal/intern/select.h leap_second.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +leap_second.o: $(hdrdir)/ruby/internal/intern/set.h leap_second.o: $(hdrdir)/ruby/internal/intern/signal.h leap_second.o: $(hdrdir)/ruby/internal/intern/sprintf.h leap_second.o: $(hdrdir)/ruby/internal/intern/string.h @@ -307,6 +310,7 @@ leap_second.o: $(hdrdir)/ruby/internal/special_consts.h leap_second.o: $(hdrdir)/ruby/internal/static_assert.h leap_second.o: $(hdrdir)/ruby/internal/stdalign.h leap_second.o: $(hdrdir)/ruby/internal/stdbool.h +leap_second.o: $(hdrdir)/ruby/internal/stdckdint.h leap_second.o: $(hdrdir)/ruby/internal/symbol.h leap_second.o: $(hdrdir)/ruby/internal/value.h leap_second.o: $(hdrdir)/ruby/internal/value_type.h @@ -451,6 +455,7 @@ new.o: $(hdrdir)/ruby/internal/intern/re.h new.o: $(hdrdir)/ruby/internal/intern/ruby.h new.o: $(hdrdir)/ruby/internal/intern/select.h new.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +new.o: $(hdrdir)/ruby/internal/intern/set.h new.o: $(hdrdir)/ruby/internal/intern/signal.h new.o: $(hdrdir)/ruby/internal/intern/sprintf.h new.o: $(hdrdir)/ruby/internal/intern/string.h @@ -470,6 +475,7 @@ new.o: $(hdrdir)/ruby/internal/special_consts.h new.o: $(hdrdir)/ruby/internal/static_assert.h new.o: $(hdrdir)/ruby/internal/stdalign.h new.o: $(hdrdir)/ruby/internal/stdbool.h +new.o: $(hdrdir)/ruby/internal/stdckdint.h new.o: $(hdrdir)/ruby/internal/symbol.h new.o: $(hdrdir)/ruby/internal/value.h new.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/time/leap_second.c b/ext/-test-/time/leap_second.c deleted file mode 100644 index ee7011fa97..0000000000 --- a/ext/-test-/time/leap_second.c +++ /dev/null @@ -1,15 +0,0 @@ -#include "ruby.h" -#include "internal/time.h" - -static VALUE -bug_time_s_reset_leap_second_info(VALUE klass) -{ - ruby_reset_leap_second_info(); - return Qnil; -} - -void -Init_time_leap_second(VALUE klass) -{ - rb_define_singleton_method(klass, "reset_leap_second_info", bug_time_s_reset_leap_second_info, 0); -} diff --git a/ext/-test-/tracepoint/depend b/ext/-test-/tracepoint/depend index 396004926e..014ba83b16 100644 --- a/ext/-test-/tracepoint/depend +++ b/ext/-test-/tracepoint/depend @@ -128,6 +128,7 @@ gc_hook.o: $(hdrdir)/ruby/internal/intern/re.h gc_hook.o: $(hdrdir)/ruby/internal/intern/ruby.h gc_hook.o: $(hdrdir)/ruby/internal/intern/select.h gc_hook.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +gc_hook.o: $(hdrdir)/ruby/internal/intern/set.h gc_hook.o: $(hdrdir)/ruby/internal/intern/signal.h gc_hook.o: $(hdrdir)/ruby/internal/intern/sprintf.h gc_hook.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ gc_hook.o: $(hdrdir)/ruby/internal/special_consts.h gc_hook.o: $(hdrdir)/ruby/internal/static_assert.h gc_hook.o: $(hdrdir)/ruby/internal/stdalign.h gc_hook.o: $(hdrdir)/ruby/internal/stdbool.h +gc_hook.o: $(hdrdir)/ruby/internal/stdckdint.h gc_hook.o: $(hdrdir)/ruby/internal/symbol.h gc_hook.o: $(hdrdir)/ruby/internal/value.h gc_hook.o: $(hdrdir)/ruby/internal/value_type.h @@ -287,6 +289,7 @@ tracepoint.o: $(hdrdir)/ruby/internal/intern/re.h tracepoint.o: $(hdrdir)/ruby/internal/intern/ruby.h tracepoint.o: $(hdrdir)/ruby/internal/intern/select.h tracepoint.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +tracepoint.o: $(hdrdir)/ruby/internal/intern/set.h tracepoint.o: $(hdrdir)/ruby/internal/intern/signal.h tracepoint.o: $(hdrdir)/ruby/internal/intern/sprintf.h tracepoint.o: $(hdrdir)/ruby/internal/intern/string.h @@ -306,6 +309,7 @@ tracepoint.o: $(hdrdir)/ruby/internal/special_consts.h tracepoint.o: $(hdrdir)/ruby/internal/static_assert.h tracepoint.o: $(hdrdir)/ruby/internal/stdalign.h tracepoint.o: $(hdrdir)/ruby/internal/stdbool.h +tracepoint.o: $(hdrdir)/ruby/internal/stdckdint.h tracepoint.o: $(hdrdir)/ruby/internal/symbol.h tracepoint.o: $(hdrdir)/ruby/internal/value.h tracepoint.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/tracepoint/gc_hook.c b/ext/-test-/tracepoint/gc_hook.c index 54c06c54a5..525be6da63 100644 --- a/ext/-test-/tracepoint/gc_hook.c +++ b/ext/-test-/tracepoint/gc_hook.c @@ -2,6 +2,7 @@ #include "ruby/debug.h" static int invoking; /* TODO: should not be global variable */ +extern VALUE tp_mBug; static VALUE invoke_proc_ensure(VALUE _) @@ -17,9 +18,9 @@ invoke_proc_begin(VALUE proc) } static void -invoke_proc(void *data) +invoke_proc(void *ivar_name) { - VALUE proc = (VALUE)data; + VALUE proc = rb_ivar_get(tp_mBug, rb_intern(ivar_name)); invoking += 1; rb_ensure(invoke_proc_begin, proc, invoke_proc_ensure, 0); } @@ -40,16 +41,16 @@ gc_start_end_i(VALUE tpval, void *data) } static VALUE -set_gc_hook(VALUE module, VALUE proc, rb_event_flag_t event, const char *tp_str, const char *proc_str) +set_gc_hook(VALUE proc, rb_event_flag_t event, const char *tp_str, const char *proc_str) { VALUE tpval; ID tp_key = rb_intern(tp_str); /* disable previous keys */ - if (rb_ivar_defined(module, tp_key) != 0 && - RTEST(tpval = rb_ivar_get(module, tp_key))) { + if (rb_ivar_defined(tp_mBug, tp_key) != 0 && + RTEST(tpval = rb_ivar_get(tp_mBug, tp_key))) { rb_tracepoint_disable(tpval); - rb_ivar_set(module, tp_key, Qnil); + rb_ivar_set(tp_mBug, tp_key, Qnil); } if (RTEST(proc)) { @@ -57,8 +58,9 @@ set_gc_hook(VALUE module, VALUE proc, rb_event_flag_t event, const char *tp_str, rb_raise(rb_eTypeError, "trace_func needs to be Proc"); } - tpval = rb_tracepoint_new(0, event, gc_start_end_i, (void *)proc); - rb_ivar_set(module, tp_key, tpval); + rb_ivar_set(tp_mBug, rb_intern(proc_str), proc); + tpval = rb_tracepoint_new(0, event, gc_start_end_i, (void *)proc_str); + rb_ivar_set(tp_mBug, tp_key, tpval); rb_tracepoint_enable(tpval); } @@ -66,16 +68,16 @@ set_gc_hook(VALUE module, VALUE proc, rb_event_flag_t event, const char *tp_str, } static VALUE -set_after_gc_start(VALUE module, VALUE proc) +set_after_gc_start(VALUE _self, VALUE proc) { - return set_gc_hook(module, proc, RUBY_INTERNAL_EVENT_GC_START, + return set_gc_hook(proc, RUBY_INTERNAL_EVENT_GC_START, "__set_after_gc_start_tpval__", "__set_after_gc_start_proc__"); } static VALUE -start_after_gc_exit(VALUE module, VALUE proc) +start_after_gc_exit(VALUE _self, VALUE proc) { - return set_gc_hook(module, proc, RUBY_INTERNAL_EVENT_GC_EXIT, + return set_gc_hook(proc, RUBY_INTERNAL_EVENT_GC_EXIT, "__set_after_gc_exit_tpval__", "__set_after_gc_exit_proc__"); } diff --git a/ext/-test-/tracepoint/tracepoint.c b/ext/-test-/tracepoint/tracepoint.c index 2826cc038c..001d9513b2 100644 --- a/ext/-test-/tracepoint/tracepoint.c +++ b/ext/-test-/tracepoint/tracepoint.c @@ -1,6 +1,8 @@ #include "ruby/ruby.h" #include "ruby/debug.h" +VALUE tp_mBug; + struct tracepoint_track { size_t newobj_count; size_t free_count; @@ -89,8 +91,8 @@ void Init_gc_hook(VALUE); void Init_tracepoint(void) { - VALUE mBug = rb_define_module("Bug"); - Init_gc_hook(mBug); - rb_define_module_function(mBug, "tracepoint_track_objspace_events", tracepoint_track_objspace_events, 0); - rb_define_module_function(mBug, "tracepoint_specify_normal_and_internal_events", tracepoint_specify_normal_and_internal_events, 0); + tp_mBug = rb_define_module("Bug"); // GC root + Init_gc_hook(tp_mBug); + rb_define_module_function(tp_mBug, "tracepoint_track_objspace_events", tracepoint_track_objspace_events, 0); + rb_define_module_function(tp_mBug, "tracepoint_specify_normal_and_internal_events", tracepoint_specify_normal_and_internal_events, 0); } diff --git a/ext/-test-/typeddata/depend b/ext/-test-/typeddata/depend index cbeafa8000..b9b4915eee 100644 --- a/ext/-test-/typeddata/depend +++ b/ext/-test-/typeddata/depend @@ -128,6 +128,7 @@ typeddata.o: $(hdrdir)/ruby/internal/intern/re.h typeddata.o: $(hdrdir)/ruby/internal/intern/ruby.h typeddata.o: $(hdrdir)/ruby/internal/intern/select.h typeddata.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +typeddata.o: $(hdrdir)/ruby/internal/intern/set.h typeddata.o: $(hdrdir)/ruby/internal/intern/signal.h typeddata.o: $(hdrdir)/ruby/internal/intern/sprintf.h typeddata.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ typeddata.o: $(hdrdir)/ruby/internal/special_consts.h typeddata.o: $(hdrdir)/ruby/internal/static_assert.h typeddata.o: $(hdrdir)/ruby/internal/stdalign.h typeddata.o: $(hdrdir)/ruby/internal/stdbool.h +typeddata.o: $(hdrdir)/ruby/internal/stdckdint.h typeddata.o: $(hdrdir)/ruby/internal/symbol.h typeddata.o: $(hdrdir)/ruby/internal/value.h typeddata.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/vm/depend b/ext/-test-/vm/depend index f0b3f3b1f4..9313f2ee36 100644 --- a/ext/-test-/vm/depend +++ b/ext/-test-/vm/depend @@ -127,6 +127,7 @@ at_exit.o: $(hdrdir)/ruby/internal/intern/re.h at_exit.o: $(hdrdir)/ruby/internal/intern/ruby.h at_exit.o: $(hdrdir)/ruby/internal/intern/select.h at_exit.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +at_exit.o: $(hdrdir)/ruby/internal/intern/set.h at_exit.o: $(hdrdir)/ruby/internal/intern/signal.h at_exit.o: $(hdrdir)/ruby/internal/intern/sprintf.h at_exit.o: $(hdrdir)/ruby/internal/intern/string.h @@ -146,6 +147,7 @@ at_exit.o: $(hdrdir)/ruby/internal/special_consts.h at_exit.o: $(hdrdir)/ruby/internal/static_assert.h at_exit.o: $(hdrdir)/ruby/internal/stdalign.h at_exit.o: $(hdrdir)/ruby/internal/stdbool.h +at_exit.o: $(hdrdir)/ruby/internal/stdckdint.h at_exit.o: $(hdrdir)/ruby/internal/symbol.h at_exit.o: $(hdrdir)/ruby/internal/value.h at_exit.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/wait/depend b/ext/-test-/wait/depend index 2e4887559c..f793d84831 100644 --- a/ext/-test-/wait/depend +++ b/ext/-test-/wait/depend @@ -137,6 +137,7 @@ wait.o: $(hdrdir)/ruby/internal/intern/re.h wait.o: $(hdrdir)/ruby/internal/intern/ruby.h wait.o: $(hdrdir)/ruby/internal/intern/select.h wait.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +wait.o: $(hdrdir)/ruby/internal/intern/set.h wait.o: $(hdrdir)/ruby/internal/intern/signal.h wait.o: $(hdrdir)/ruby/internal/intern/sprintf.h wait.o: $(hdrdir)/ruby/internal/intern/string.h @@ -156,6 +157,7 @@ wait.o: $(hdrdir)/ruby/internal/special_consts.h wait.o: $(hdrdir)/ruby/internal/static_assert.h wait.o: $(hdrdir)/ruby/internal/stdalign.h wait.o: $(hdrdir)/ruby/internal/stdbool.h +wait.o: $(hdrdir)/ruby/internal/stdckdint.h wait.o: $(hdrdir)/ruby/internal/symbol.h wait.o: $(hdrdir)/ruby/internal/value.h wait.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/win32/dln/extconf.rb b/ext/-test-/win32/dln/extconf.rb index d72b6a868c..bea52cfec3 100644 --- a/ext/-test-/win32/dln/extconf.rb +++ b/ext/-test-/win32/dln/extconf.rb @@ -7,6 +7,7 @@ if $mingw or $mswin testdll = "dlntest.dll" $cleanfiles << testdll $cleanfiles << "dlntest.#{$LIBEXT}" + $cleanfiles << "libdlntest.#{$OBJEXT}" config_string('cleanobjs') {|t| $cleanfiles.concat(t.gsub(/\$\*/, 'dlntest').split)} create_makefile("-test-/win32/dln") do |m| diff --git a/ext/.document b/ext/.document index 0efd511a61..374abe6580 100644 --- a/ext/.document +++ b/ext/.document @@ -76,8 +76,6 @@ openssl/ossl_x509name.c openssl/ossl_x509req.c openssl/ossl_x509revoked.c openssl/ossl_x509store.c -pathname/lib -pathname/pathname.c psych/lib psych/psych.c psych/psych_emitter.c @@ -92,6 +90,7 @@ readline/readline.c ripper/lib socket stringio/stringio.c +strscan/lib strscan/strscan.c syslog/syslog.c syslog/lib diff --git a/ext/cgi/escape/depend b/ext/cgi/escape/depend index 37304a24f4..05b59bfdea 100644 --- a/ext/cgi/escape/depend +++ b/ext/cgi/escape/depend @@ -138,6 +138,7 @@ escape.o: $(hdrdir)/ruby/internal/intern/re.h escape.o: $(hdrdir)/ruby/internal/intern/ruby.h escape.o: $(hdrdir)/ruby/internal/intern/select.h escape.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +escape.o: $(hdrdir)/ruby/internal/intern/set.h escape.o: $(hdrdir)/ruby/internal/intern/signal.h escape.o: $(hdrdir)/ruby/internal/intern/sprintf.h escape.o: $(hdrdir)/ruby/internal/intern/string.h @@ -157,6 +158,7 @@ escape.o: $(hdrdir)/ruby/internal/special_consts.h escape.o: $(hdrdir)/ruby/internal/static_assert.h escape.o: $(hdrdir)/ruby/internal/stdalign.h escape.o: $(hdrdir)/ruby/internal/stdbool.h +escape.o: $(hdrdir)/ruby/internal/stdckdint.h escape.o: $(hdrdir)/ruby/internal/symbol.h escape.o: $(hdrdir)/ruby/internal/value.h escape.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/cgi/escape/escape.c b/ext/cgi/escape/escape.c index 495ad83aa3..4773186603 100644 --- a/ext/cgi/escape/escape.c +++ b/ext/cgi/escape/escape.c @@ -8,7 +8,7 @@ RUBY_EXTERN const signed char ruby_digit36_to_number_table[]; #define upper_hexdigits (ruby_hexdigits+16) #define char_to_number(c) ruby_digit36_to_number_table[(unsigned char)(c)] -static VALUE rb_cCGI, rb_mUtil, rb_mEscape; +static VALUE rb_cCGI, rb_mEscape, rb_mEscapeExt; static ID id_accept_charset; #define HTML_ESCAPE_MAX_LEN 6 @@ -45,6 +45,7 @@ escaped_length(VALUE str) static VALUE optimized_escape_html(VALUE str) { + VALUE escaped; VALUE vbuf; char *buf = ALLOCV_N(char, vbuf, escaped_length(str)); const char *cstr = RSTRING_PTR(str); @@ -63,7 +64,6 @@ optimized_escape_html(VALUE str) } } - VALUE escaped; if (RSTRING_LEN(str) < (dest - buf)) { escaped = rb_str_new(buf, dest - buf); preserve_original_state(str, escaped); @@ -471,17 +471,17 @@ Init_escape(void) void InitVM_escape(void) { - rb_cCGI = rb_define_class("CGI", rb_cObject); - rb_mEscape = rb_define_module_under(rb_cCGI, "Escape"); - rb_mUtil = rb_define_module_under(rb_cCGI, "Util"); - rb_define_method(rb_mEscape, "escapeHTML", cgiesc_escape_html, 1); - rb_define_method(rb_mEscape, "unescapeHTML", cgiesc_unescape_html, 1); - rb_define_method(rb_mEscape, "escapeURIComponent", cgiesc_escape_uri_component, 1); - rb_define_alias(rb_mEscape, "escape_uri_component", "escapeURIComponent"); - rb_define_method(rb_mEscape, "unescapeURIComponent", cgiesc_unescape_uri_component, -1); - rb_define_alias(rb_mEscape, "unescape_uri_component", "unescapeURIComponent"); - rb_define_method(rb_mEscape, "escape", cgiesc_escape, 1); - rb_define_method(rb_mEscape, "unescape", cgiesc_unescape, -1); - rb_prepend_module(rb_mUtil, rb_mEscape); - rb_extend_object(rb_cCGI, rb_mEscape); + rb_cCGI = rb_define_class("CGI", rb_cObject); + rb_mEscapeExt = rb_define_module_under(rb_cCGI, "EscapeExt"); + rb_mEscape = rb_define_module_under(rb_cCGI, "Escape"); + rb_define_method(rb_mEscapeExt, "escapeHTML", cgiesc_escape_html, 1); + rb_define_method(rb_mEscapeExt, "unescapeHTML", cgiesc_unescape_html, 1); + rb_define_method(rb_mEscapeExt, "escapeURIComponent", cgiesc_escape_uri_component, 1); + rb_define_alias(rb_mEscapeExt, "escape_uri_component", "escapeURIComponent"); + rb_define_method(rb_mEscapeExt, "unescapeURIComponent", cgiesc_unescape_uri_component, -1); + rb_define_alias(rb_mEscapeExt, "unescape_uri_component", "unescapeURIComponent"); + rb_define_method(rb_mEscapeExt, "escape", cgiesc_escape, 1); + rb_define_method(rb_mEscapeExt, "unescape", cgiesc_unescape, -1); + rb_prepend_module(rb_mEscape, rb_mEscapeExt); + rb_extend_object(rb_cCGI, rb_mEscapeExt); } diff --git a/ext/continuation/depend b/ext/continuation/depend index f0333d7fe6..b395d3d4d7 100644 --- a/ext/continuation/depend +++ b/ext/continuation/depend @@ -127,6 +127,7 @@ continuation.o: $(hdrdir)/ruby/internal/intern/re.h continuation.o: $(hdrdir)/ruby/internal/intern/ruby.h continuation.o: $(hdrdir)/ruby/internal/intern/select.h continuation.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +continuation.o: $(hdrdir)/ruby/internal/intern/set.h continuation.o: $(hdrdir)/ruby/internal/intern/signal.h continuation.o: $(hdrdir)/ruby/internal/intern/sprintf.h continuation.o: $(hdrdir)/ruby/internal/intern/string.h @@ -146,6 +147,7 @@ continuation.o: $(hdrdir)/ruby/internal/special_consts.h continuation.o: $(hdrdir)/ruby/internal/static_assert.h continuation.o: $(hdrdir)/ruby/internal/stdalign.h continuation.o: $(hdrdir)/ruby/internal/stdbool.h +continuation.o: $(hdrdir)/ruby/internal/stdckdint.h continuation.o: $(hdrdir)/ruby/internal/symbol.h continuation.o: $(hdrdir)/ruby/internal/value.h continuation.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/coverage/coverage.c b/ext/coverage/coverage.c index a3381901ee..747f065fba 100644 --- a/ext/coverage/coverage.c +++ b/ext/coverage/coverage.c @@ -52,17 +52,29 @@ rb_coverage_supported(VALUE self, VALUE _mode) /* * call-seq: - * Coverage.setup => nil - * Coverage.setup(:all) => nil - * Coverage.setup(lines: bool, branches: bool, methods: bool, eval: bool) => nil - * Coverage.setup(oneshot_lines: true) => nil + * Coverage.setup -> nil + * Coverage.setup(type) -> nil + * Coverage.setup(lines: false, branches: false, methods: false, eval: false, oneshot_lines: false) -> nil * - * Set up the coverage measurement. + * Performs setup for coverage measurement, but does not start coverage measurement. + * To start coverage measurement, use Coverage.resume. * - * Note that this method does not start the measurement itself. - * Use Coverage.resume to start the measurement. + * To perform both setup and start coverage measurement, Coverage.start can be used. * - * You may want to use Coverage.start to setup and then start the measurement. + * With argument +type+ given and +type+ is symbol +:all+, enables all types of coverage + * (lines, branches, methods, and eval). + * + * Keyword arguments or hash +type+ can be given with each of the following keys: + * + * - +lines+: Enables line coverage that records the number of times each line was executed. + * If +lines+ is enabled, +oneshot_lines+ cannot be enabled. + * See {Lines Coverage}[rdoc-ref:Coverage@Lines+Coverage]. + * - +branches+: Enables branch coverage that records the number of times each + * branch in each conditional was executed. See {Branches Coverage}[rdoc-ref:Coverage@Branch+Coverage]. + * - +methods+: Enables method coverage that records the number of times each method was exectued. + * See {Methods Coverage}[rdoc-ref:Coverage@Methods+Coverage]. + * - +eval+: Enables coverage for evaluations (e.g. Kernel#eval, Module#class_eval). + * See {Eval Coverage}[rdoc-ref:Coverage@Eval+Coverage]. */ static VALUE rb_coverage_setup(int argc, VALUE *argv, VALUE klass) @@ -243,8 +255,8 @@ method_coverage_i(void *vstart, void *vend, size_t stride, void *data) VALUE ncoverages = *(VALUE*)data, v; for (v = (VALUE)vstart; v != (VALUE)vend; v += stride) { - void *poisoned = asan_poisoned_object_p(v); - asan_unpoison_object(v, false); + void *poisoned = rb_asan_poisoned_object_p(v); + rb_asan_unpoison_object(v, false); if (RB_TYPE_P(v, T_IMEMO) && imemo_type(v) == imemo_ment) { const rb_method_entry_t *me = (rb_method_entry_t *) v; @@ -287,7 +299,7 @@ method_coverage_i(void *vstart, void *vend, size_t stride, void *data) } if (poisoned) { - asan_poison_object(v); + rb_asan_poison_object(v); } } return 0; @@ -337,7 +349,7 @@ coverage_peek_result_i(st_data_t key, st_data_t val, st_data_t h) * Coverage.peek_result => hash * * Returns a hash that contains filename as key and coverage array as value. - * This is the same as `Coverage.result(stop: false, clear: false)`. + * This is the same as <tt>Coverage.result(stop: false, clear: false)</tt>. * * { * "file.rb" => [1, 2, nil], @@ -352,8 +364,8 @@ rb_coverage_peek_result(VALUE klass) if (!RTEST(coverages)) { rb_raise(rb_eRuntimeError, "coverage measurement is not enabled"); } - OBJ_WB_UNPROTECT(coverages); - st_foreach(RHASH_TBL_RAW(coverages), coverage_peek_result_i, ncoverages); + + rb_hash_foreach(coverages, coverage_peek_result_i, ncoverages); if (current_mode & COVERAGE_TARGET_METHODS) { rb_objspace_each_objects(method_coverage_i, &ncoverages); @@ -467,7 +479,7 @@ rb_coverage_running(VALUE klass) return current_state == RUNNING ? Qtrue : Qfalse; } -/* Coverage provides coverage measurement feature for Ruby. +/* \Coverage provides coverage measurement feature for Ruby. * This feature is experimental, so these APIs may be changed in future. * * Caveat: Currently, only process-global coverage measurement is supported. @@ -503,7 +515,7 @@ rb_coverage_running(VALUE klass) * require "foo.rb" * p Coverage.result #=> {"foo.rb"=>[1, 1, 10, nil, nil, 1, 1, nil, 0, nil]} * - * == Lines Coverage + * == Lines \Coverage * * If a coverage mode is not explicitly specified when starting coverage, lines * coverage is what will run. It reports the number of line executions for each @@ -523,7 +535,7 @@ rb_coverage_running(VALUE klass) * A +nil+ value means coverage is disabled for this line (lines like +else+ * and +end+). * - * == Oneshot Lines Coverage + * == Oneshot Lines \Coverage * * Oneshot lines coverage tracks and reports on the executed lines while * coverage is running. It will not report how many times a line was executed, @@ -537,7 +549,7 @@ rb_coverage_running(VALUE klass) * The value of the oneshot lines coverage result is an array containing the * line numbers that were executed. * - * == Branches Coverage + * == Branches \Coverage * * Branches coverage reports how many times each branch within each conditional * was executed. @@ -562,7 +574,7 @@ rb_coverage_running(VALUE klass) * 5. The ending line number it appears on in the file. * 6. The ending column number it appears on in the file. * - * == Methods Coverage + * == Methods \Coverage * * Methods coverage reports how many times each method was executed. * @@ -600,7 +612,63 @@ rb_coverage_running(VALUE klass) * 5. The ending line number the method appears on in the file. * 6. The ending column number the method appears on in the file. * - * == All Coverage Modes + * == Eval \Coverage + * + * Eval coverage can be combined with the coverage types above to track + * coverage for eval. + * + * require "coverage" + * Coverage.start(eval: true, lines: true) + * + * eval(<<~RUBY, nil, "eval 1") + * ary = [] + * 10.times do |i| + * ary << "hello" * i + * end + * RUBY + * + * Coverage.result # => {"eval 1" => {lines: [1, 1, 10, nil]}} + * + * Note that the eval must have a filename assigned, otherwise coverage + * will not be measured. + * + * require "coverage" + * Coverage.start(eval: true, lines: true) + * + * eval(<<~RUBY) + * ary = [] + * 10.times do |i| + * ary << "hello" * i + * end + * RUBY + * + * Coverage.result # => {"(eval)" => {lines: [nil, nil, nil, nil]}} + * + * Also note that if a line number is assigned to the eval and it is not 1, + * then the resulting coverage will be padded with +nil+ if the line number is + * greater than 1, and truncated if the line number is less than 1. + * + * require "coverage" + * Coverage.start(eval: true, lines: true) + * + * eval(<<~RUBY, nil, "eval 1", 3) + * ary = [] + * 10.times do |i| + * ary << "hello" * i + * end + * RUBY + * + * eval(<<~RUBY, nil, "eval 2", -1) + * ary = [] + * 10.times do |i| + * ary << "hello" * i + * end + * RUBY + * + * Coverage.result + * # => {"eval 1" => {lines: [nil, nil, 1, 1, 10, nil]}, "eval 2" => {lines: [10, nil]}} + * + * == All \Coverage Modes * * You can also run all modes of coverage simultaneously with this shortcut. * Note that running all coverage modes does not run both lines and oneshot diff --git a/ext/coverage/depend b/ext/coverage/depend index 0a6c61d5c6..fb7f9a95af 100644 --- a/ext/coverage/depend +++ b/ext/coverage/depend @@ -140,6 +140,7 @@ coverage.o: $(hdrdir)/ruby/internal/intern/re.h coverage.o: $(hdrdir)/ruby/internal/intern/ruby.h coverage.o: $(hdrdir)/ruby/internal/intern/select.h coverage.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +coverage.o: $(hdrdir)/ruby/internal/intern/set.h coverage.o: $(hdrdir)/ruby/internal/intern/signal.h coverage.o: $(hdrdir)/ruby/internal/intern/sprintf.h coverage.o: $(hdrdir)/ruby/internal/intern/string.h @@ -159,6 +160,7 @@ coverage.o: $(hdrdir)/ruby/internal/special_consts.h coverage.o: $(hdrdir)/ruby/internal/static_assert.h coverage.o: $(hdrdir)/ruby/internal/stdalign.h coverage.o: $(hdrdir)/ruby/internal/stdbool.h +coverage.o: $(hdrdir)/ruby/internal/stdckdint.h coverage.o: $(hdrdir)/ruby/internal/symbol.h coverage.o: $(hdrdir)/ruby/internal/value.h coverage.o: $(hdrdir)/ruby/internal/value_type.h @@ -181,12 +183,14 @@ coverage.o: $(top_srcdir)/id_table.h coverage.o: $(top_srcdir)/internal.h coverage.o: $(top_srcdir)/internal/array.h coverage.o: $(top_srcdir)/internal/basic_operators.h +coverage.o: $(top_srcdir)/internal/box.h coverage.o: $(top_srcdir)/internal/compilers.h coverage.o: $(top_srcdir)/internal/gc.h coverage.o: $(top_srcdir)/internal/hash.h coverage.o: $(top_srcdir)/internal/imemo.h coverage.o: $(top_srcdir)/internal/sanitizers.h coverage.o: $(top_srcdir)/internal/serial.h +coverage.o: $(top_srcdir)/internal/set_table.h coverage.o: $(top_srcdir)/internal/static_assert.h coverage.o: $(top_srcdir)/internal/thread.h coverage.o: $(top_srcdir)/internal/variable.h diff --git a/ext/coverage/lib/coverage.rb b/ext/coverage/lib/coverage.rb index f1923ef366..4bd20e22cb 100644 --- a/ext/coverage/lib/coverage.rb +++ b/ext/coverage/lib/coverage.rb @@ -1,6 +1,11 @@ require "coverage.so" module Coverage + # call-seq: + # line_stub(file) -> array + # + # A simple helper function that creates the "stub" of line coverage + # from a given source code. def self.line_stub(file) lines = File.foreach(file).map { nil } iseqs = [RubyVM::InstructionSequence.compile_file(file)] diff --git a/ext/date/date.gemspec b/ext/date/date.gemspec index bd42b1518a..cb439bd0a5 100644 --- a/ext/date/date.gemspec +++ b/ext/date/date.gemspec @@ -7,8 +7,8 @@ end Gem::Specification.new do |s| s.name = "date" s.version = version - s.summary = "A subclass of Object includes Comparable module for handling dates." - s.description = "A subclass of Object includes Comparable module for handling dates." + s.summary = "The official date library for Ruby." + s.description = "The official date library for Ruby." if Gem::Platform === s.platform and s.platform =~ 'java' or RUBY_ENGINE == 'jruby' s.platform = 'java' @@ -17,7 +17,7 @@ Gem::Specification.new do |s| s.require_path = %w{lib} s.files = [ - "README.md", + "README.md", "COPYING", "BSDL", "lib/date.rb", "ext/date/date_core.c", "ext/date/date_parse.c", "ext/date/date_strftime.c", "ext/date/date_strptime.c", "ext/date/date_tmx.h", "ext/date/extconf.rb", "ext/date/prereq.mk", "ext/date/zonetab.h", "ext/date/zonetab.list" diff --git a/ext/date/date_core.c b/ext/date/date_core.c index f4b390584b..9755fb819e 100644 --- a/ext/date/date_core.c +++ b/ext/date/date_core.c @@ -57,7 +57,7 @@ static VALUE sym_hour, sym_min, sym_sec, sym_sec_fraction, sym_zone; #define f_add3(x,y,z) f_add(f_add(x, y), z) #define f_sub3(x,y,z) f_sub(f_sub(x, y), z) -#define f_frozen_ary(...) rb_obj_freeze(rb_ary_new3(__VA_ARGS__)) +#define f_frozen_ary(...) rb_ary_freeze(rb_ary_new3(__VA_ARGS__)) static VALUE date_initialize(int argc, VALUE *argv, VALUE self); static VALUE datetime_initialize(int argc, VALUE *argv, VALUE self); @@ -248,6 +248,11 @@ f_negative_p(VALUE x) #define date_sg_t double #endif +#define JULIAN_EPOCH_DATE "-4712-01-01" +#define JULIAN_EPOCH_DATETIME JULIAN_EPOCH_DATE "T00:00:00+00:00" +#define JULIAN_EPOCH_DATETIME_RFC3339 "Mon, 1 Jan -4712 00:00:00 +0000" +#define JULIAN_EPOCH_DATETIME_HTTPDATE "Mon, 01 Jan -4712 00:00:00 GMT" + /* A set of nth, jd, df and sf denote ajd + 1/2. Each ajd begin at * noon of GMT (assume equal to UTC). However, this begins at * midnight. @@ -447,11 +452,43 @@ do {\ static int c_valid_civil_p(int, int, int, double, int *, int *, int *, int *); +/* Check if using pure Gregorian calendar (sg == -Infinity) */ +#define c_gregorian_only_p(sg) (isinf(sg) && (sg) < 0) + +/* + * Fast path macros for pure Gregorian calendar. + * Sets *rjd to the JD value, *ns to 1 (new style), and returns. + */ +#define GREGORIAN_JD_FAST_PATH_RET(sg, jd_expr, rjd, ns) \ + if (c_gregorian_only_p(sg)) { \ + *(rjd) = (jd_expr); \ + *(ns) = 1; \ + return 1; \ + } + +#define GREGORIAN_JD_FAST_PATH(sg, jd_expr, rjd, ns) \ + if (c_gregorian_only_p(sg)) { \ + *(rjd) = (jd_expr); \ + *(ns) = 1; \ + return; \ + } + +/* Forward declarations for Neri-Schneider optimized functions */ +static int c_gregorian_civil_to_jd(int y, int m, int d); +static void c_gregorian_jd_to_civil(int jd, int *ry, int *rm, int *rd); +static int c_gregorian_fdoy(int y); +static int c_gregorian_ldoy(int y); +static int c_gregorian_ldom_jd(int y, int m); +static int ns_jd_in_range(int jd); + static int c_find_fdoy(int y, double sg, int *rjd, int *ns) { int d, rm, rd; + GREGORIAN_JD_FAST_PATH_RET(sg, c_gregorian_fdoy(y), rjd, ns); + + /* Keep existing loop for Julian/reform period */ for (d = 1; d < 31; d++) if (c_valid_civil_p(y, 1, d, sg, &rm, &rd, rjd, ns)) return 1; @@ -463,6 +500,9 @@ c_find_ldoy(int y, double sg, int *rjd, int *ns) { int i, rm, rd; + GREGORIAN_JD_FAST_PATH_RET(sg, c_gregorian_ldoy(y), rjd, ns); + + /* Keep existing loop for Julian/reform period */ for (i = 0; i < 30; i++) if (c_valid_civil_p(y, 12, 31 - i, sg, &rm, &rd, rjd, ns)) return 1; @@ -488,6 +528,9 @@ c_find_ldom(int y, int m, double sg, int *rjd, int *ns) { int i, rm, rd; + GREGORIAN_JD_FAST_PATH_RET(sg, c_gregorian_ldom_jd(y, m), rjd, ns); + + /* Keep existing loop for Julian/reform period */ for (i = 0; i < 30; i++) if (c_valid_civil_p(y, m, 31 - i, sg, &rm, &rd, rjd, ns)) return 1; @@ -497,55 +540,69 @@ c_find_ldom(int y, int m, double sg, int *rjd, int *ns) static void c_civil_to_jd(int y, int m, int d, double sg, int *rjd, int *ns) { - double a, b, jd; + int jd; + + GREGORIAN_JD_FAST_PATH(sg, c_gregorian_civil_to_jd(y, m, d), rjd, ns); + + /* Calculate Gregorian JD using optimized algorithm */ + jd = c_gregorian_civil_to_jd(y, m, d); - if (m <= 2) { - y -= 1; - m += 12; - } - a = floor(y / 100.0); - b = 2 - a + floor(a / 4.0); - jd = floor(365.25 * (y + 4716)) + - floor(30.6001 * (m + 1)) + - d + b - 1524; if (jd < sg) { - jd -= b; + /* Before Gregorian switchover - use Julian calendar */ + int y2 = y, m2 = m; + if (m2 <= 2) { + y2 -= 1; + m2 += 12; + } + jd = (int)(floor(365.25 * (y2 + 4716)) + + floor(30.6001 * (m2 + 1)) + + d - 1524); *ns = 0; } - else + else { *ns = 1; + } - *rjd = (int)jd; + *rjd = jd; } static void c_jd_to_civil(int jd, double sg, int *ry, int *rm, int *rdom) { - double x, a, b, c, d, e, y, m, dom; - - if (jd < sg) - a = jd; - else { - x = floor((jd - 1867216.25) / 36524.25); - a = jd + 1 + x - floor(x / 4.0); - } - b = a + 1524; - c = floor((b - 122.1) / 365.25); - d = floor(365.25 * c); - e = floor((b - d) / 30.6001); - dom = b - d - floor(30.6001 * e); - if (e <= 13) { - m = e - 1; - y = c - 4716; - } - else { - m = e - 13; - y = c - 4715; + /* Fast path: pure Gregorian or date after switchover, within safe range */ + if ((c_gregorian_only_p(sg) || jd >= sg) && ns_jd_in_range(jd)) { + c_gregorian_jd_to_civil(jd, ry, rm, rdom); + return; } - *ry = (int)y; - *rm = (int)m; - *rdom = (int)dom; + /* Original algorithm for Julian calendar or extreme dates */ + { + double x, a, b, c, d, e, y, m, dom; + + if (jd < sg) + a = jd; + else { + x = floor((jd - 1867216.25) / 36524.25); + a = jd + 1 + x - floor(x / 4.0); + } + b = a + 1524; + c = floor((b - 122.1) / 365.25); + d = floor(365.25 * c); + e = floor((b - d) / 30.6001); + dom = b - d - floor(30.6001 * e); + if (e <= 13) { + m = e - 1; + y = c - 4716; + } + else { + m = e - 13; + y = c - 4715; + } + + *ry = (int)y; + *rm = (int)m; + *rdom = (int)dom; + } } static void @@ -720,6 +777,147 @@ c_gregorian_last_day_of_month(int y, int m) return monthtab[c_gregorian_leap_p(y) ? 1 : 0][m]; } +/* + * Neri-Schneider algorithm for optimized Gregorian date conversion. + * Reference: Neri & Schneider, "Euclidean Affine Functions and Applications + * to Calendar Algorithms", Software: Practice and Experience, 2023. + * https://arxiv.org/abs/2102.06959 + * + * This algorithm provides ~2-3x speedup over traditional floating-point + * implementations by using pure integer arithmetic with multiplication + * and bit-shifts instead of expensive division operations. + */ + +/* JDN of March 1, Year 0 in proleptic Gregorian calendar */ +#define NS_EPOCH 1721120 + +/* Days in a 4-year cycle (3 normal years + 1 leap year) */ +#define NS_DAYS_IN_4_YEARS 1461 + +/* Days in a 400-year Gregorian cycle (97 leap years in 400 years) */ +#define NS_DAYS_IN_400_YEARS 146097 + +/* Years per century */ +#define NS_YEARS_PER_CENTURY 100 + +/* + * Multiplier for extracting year within century using fixed-point arithmetic. + * This is ceil(2^32 / NS_DAYS_IN_4_YEARS) for the Euclidean affine function. + */ +#define NS_YEAR_MULTIPLIER 2939745 + +/* + * Coefficients for month calculation from day-of-year. + * Maps day-of-year to month using: month = (NS_MONTH_COEFF * doy + NS_MONTH_OFFSET) >> 16 + */ +#define NS_MONTH_COEFF 2141 +#define NS_MONTH_OFFSET 197913 + +/* + * Coefficients for civil date to JDN month contribution. + * Maps month to accumulated days: days = (NS_CIVIL_MONTH_COEFF * m - NS_CIVIL_MONTH_OFFSET) / 32 + */ +#define NS_CIVIL_MONTH_COEFF 979 +#define NS_CIVIL_MONTH_OFFSET 2919 +#define NS_CIVIL_MONTH_DIVISOR 32 + +/* Days from March 1 to December 31 (for Jan/Feb year adjustment) */ +#define NS_DAYS_BEFORE_NEW_YEAR 306 + +/* + * Safe bounds for Neri-Schneider algorithm to avoid integer overflow. + * These correspond to approximately years -1,000,000 to +1,000,000. + */ +#define NS_JD_MIN -364000000 +#define NS_JD_MAX 538000000 + +inline static int +ns_jd_in_range(int jd) +{ + return jd >= NS_JD_MIN && jd <= NS_JD_MAX; +} + +/* Optimized: Gregorian date -> Julian Day Number */ +static int +c_gregorian_civil_to_jd(int y, int m, int d) +{ + /* Shift epoch to March 1 of year 0 (Jan/Feb belong to previous year) */ + int j = (m < 3) ? 1 : 0; + int y0 = y - j; + int m0 = j ? m + 12 : m; + int d0 = d - 1; + + /* Calculate year contribution with leap year correction */ + int q1 = DIV(y0, NS_YEARS_PER_CENTURY); + int yc = DIV(NS_DAYS_IN_4_YEARS * y0, 4) - q1 + DIV(q1, 4); + + /* Calculate month contribution using integer arithmetic */ + int mc = (NS_CIVIL_MONTH_COEFF * m0 - NS_CIVIL_MONTH_OFFSET) / NS_CIVIL_MONTH_DIVISOR; + + /* Combine and add epoch offset to get JDN */ + return yc + mc + d0 + NS_EPOCH; +} + +/* Optimized: Julian Day Number -> Gregorian date */ +static void +c_gregorian_jd_to_civil(int jd, int *ry, int *rm, int *rd) +{ + int r0, n1, q1, r1, n2, q2, r2, n3, q3, r3, y0, j; + uint64_t u2; + + /* Convert JDN to rata die (March 1, Year 0 epoch) */ + r0 = jd - NS_EPOCH; + + /* Extract century and day within 400-year cycle */ + /* Use Euclidean (floor) division for negative values */ + n1 = 4 * r0 + 3; + q1 = DIV(n1, NS_DAYS_IN_400_YEARS); + r1 = MOD(n1, NS_DAYS_IN_400_YEARS) / 4; + + /* Calculate year within century and day of year */ + n2 = 4 * r1 + 3; + /* Use 64-bit arithmetic to avoid overflow */ + u2 = (uint64_t)NS_YEAR_MULTIPLIER * (uint64_t)n2; + q2 = (int)(u2 >> 32); + r2 = (int)((uint32_t)u2 / NS_YEAR_MULTIPLIER / 4); + + /* Calculate month and day using integer arithmetic */ + n3 = NS_MONTH_COEFF * r2 + NS_MONTH_OFFSET; + q3 = n3 >> 16; + r3 = (n3 & 0xFFFF) / NS_MONTH_COEFF; + + /* Combine century and year */ + y0 = NS_YEARS_PER_CENTURY * q1 + q2; + + /* Adjust for January/February (shift from fiscal year) */ + j = (r2 >= NS_DAYS_BEFORE_NEW_YEAR) ? 1 : 0; + + *ry = y0 + j; + *rm = j ? q3 - 12 : q3; + *rd = r3 + 1; +} + +/* O(1) first day of year for Gregorian calendar */ +inline static int +c_gregorian_fdoy(int y) +{ + return c_gregorian_civil_to_jd(y, 1, 1); +} + +/* O(1) last day of year for Gregorian calendar */ +inline static int +c_gregorian_ldoy(int y) +{ + return c_gregorian_civil_to_jd(y, 12, 31); +} + +/* O(1) last day of month (JDN) for Gregorian calendar */ +inline static int +c_gregorian_ldom_jd(int y, int m) +{ + return c_gregorian_civil_to_jd(y, m, c_gregorian_last_day_of_month(y, m)); +} + static int c_valid_julian_p(int y, int m, int d, int *rm, int *rd) { @@ -1594,7 +1792,7 @@ m_ajd(union DateData *x) if (simple_dat_p(x)) { r = m_real_jd(x); - if (FIXNUM_P(r) && FIX2LONG(r) <= (FIXNUM_MAX / 2)) { + if (FIXNUM_P(r) && FIX2LONG(r) <= (FIXNUM_MAX / 2) && FIX2LONG(r) >= (FIXNUM_MIN + 1) / 2) { long ir = FIX2LONG(r); ir = ir * 2 - 1; return rb_rational_new2(LONG2FIX(ir), INT2FIX(2)); @@ -2493,7 +2691,7 @@ date_s__valid_jd_p(int argc, VALUE *argv, VALUE klass) * * Date.valid_jd?(2451944) # => true * - * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * * Related: Date.jd. */ @@ -2587,7 +2785,7 @@ date_s__valid_civil_p(int argc, VALUE *argv, VALUE klass) * Date.valid_date?(2001, 2, 29) # => false * Date.valid_date?(2001, 2, -1) # => true * - * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * * Related: Date.jd, Date.new. */ @@ -2675,7 +2873,7 @@ date_s__valid_ordinal_p(int argc, VALUE *argv, VALUE klass) * Date.valid_ordinal?(2001, 34) # => true * Date.valid_ordinal?(2001, 366) # => false * - * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * * Related: Date.jd, Date.ordinal. */ @@ -2765,7 +2963,7 @@ date_s__valid_commercial_p(int argc, VALUE *argv, VALUE klass) * * See Date.commercial. * - * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * * Related: Date.jd, Date.commercial. */ @@ -3345,7 +3543,7 @@ static VALUE d_lite_plus(VALUE, VALUE); * * Date.jd(Date::ITALY - 1).julian? # => true * - * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * * Related: Date.new. */ @@ -3410,7 +3608,7 @@ date_s_jd(int argc, VALUE *argv, VALUE klass) * * Raises an exception if +yday+ is zero or out of range. * - * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * * Related: Date.jd, Date.new. */ @@ -3487,7 +3685,7 @@ date_s_civil(int argc, VALUE *argv, VALUE klass) * where +n+ is the number of days in the month; * when the argument is negative, counts backward from the end of the month. * - * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * * Related: Date.jd. */ @@ -3593,7 +3791,7 @@ date_initialize(int argc, VALUE *argv, VALUE self) * Date.commercial(2020, 1, 1).to_s # => "2019-12-30" Date.commercial(2020, 1, 7).to_s # => "2020-01-05" * - * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * * Related: Date.jd, Date.new, Date.ordinal. */ @@ -3778,7 +3976,7 @@ static void set_sg(union DateData *, double); * * Date.today.to_s # => "2022-07-06" * - * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * */ static VALUE @@ -3873,7 +4071,6 @@ static VALUE rt_complete_frags(VALUE klass, VALUE hash) { static VALUE tab = Qnil; - int g; long e; VALUE k, a, d; @@ -3970,9 +4167,13 @@ rt_complete_frags(VALUE klass, VALUE hash) rb_gc_register_mark_object(tab); } + k = a = Qnil; + { - long i, eno = 0, idx = 0; + long i, eno = 0; + VALUE t = Qnil; + e = 0; for (i = 0; i < RARRAY_LEN(tab); i++) { VALUE x, a; @@ -3987,23 +4188,20 @@ rt_complete_frags(VALUE klass, VALUE hash) n++; if (n > eno) { eno = n; - idx = i; + t = x; } } } - if (eno == 0) - g = 0; - else { - g = 1; - k = RARRAY_AREF(RARRAY_AREF(tab, idx), 0); - a = RARRAY_AREF(RARRAY_AREF(tab, idx), 1); - e = eno; + if (eno > 0) { + k = RARRAY_AREF(t, 0); + a = RARRAY_AREF(t, 1); } + e = eno; } d = Qnil; - if (g && !NIL_P(k) && (RARRAY_LEN(a) - e)) { + if (!NIL_P(k) && (RARRAY_LEN(a) > e)) { if (k == sym("ordinal")) { if (NIL_P(ref_hash("year"))) { if (NIL_P(d)) @@ -4090,7 +4288,7 @@ rt_complete_frags(VALUE klass, VALUE hash) } } - if (g && k == sym("time")) { + if (k == sym("time")) { if (f_le_p(klass, cDateTime)) { if (NIL_P(d)) d = date_s_today(0, (VALUE *)0, cDate); @@ -4378,7 +4576,7 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass, * Date._strptime('2001-02-03', '%Y-%m-%d') # => {:year=>2001, :mon=>2, :mday=>3} * * For other formats, see - * {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]. + * {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]. * (Unlike Date.strftime, does not support flags and width.) * * See also {strptime(3)}[https://man7.org/linux/man-pages/man3/strptime.3.html]. @@ -4407,10 +4605,10 @@ date_s__strptime(int argc, VALUE *argv, VALUE klass) * Date.strptime('sat3feb01', '%a%d%b%y') # => #<Date: 2001-02-03> * * For other formats, see - * {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]. + * {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]. * (Unlike Date.strftime, does not support flags and width.) * - * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * * See also {strptime(3)}[https://man7.org/linux/man-pages/man3/strptime.3.html]. * @@ -4425,7 +4623,7 @@ date_s_strptime(int argc, VALUE *argv, VALUE klass) switch (argc) { case 0: - str = rb_str_new2("-4712-01-01"); + str = rb_str_new2(JULIAN_EPOCH_DATE); case 1: fmt = rb_str_new2("%F"); case 2: @@ -4459,17 +4657,10 @@ get_limit(VALUE opt) #define rb_category_warn(category, fmt) rb_warn(fmt) #endif -static void +static VALUE check_limit(VALUE str, VALUE opt) { size_t slen, limit; - if (NIL_P(str)) return; - if (SYMBOL_P(str)) { - rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, - "The ability to parse Symbol is an unintentional bug and is deprecated"); - str = rb_sym2str(str); - } - StringValue(str); slen = RSTRING_LEN(str); limit = get_limit(opt); @@ -4477,6 +4668,7 @@ check_limit(VALUE str, VALUE opt) rb_raise(rb_eArgError, "string length (%"PRI_SIZE_PREFIX"u) exceeds the limit %"PRI_SIZE_PREFIX"u", slen, limit); } + return str; } static VALUE @@ -4484,10 +4676,8 @@ date_s__parse_internal(int argc, VALUE *argv, VALUE klass) { VALUE vstr, vcomp, hash, opt; - rb_scan_args(argc, argv, "11:", &vstr, &vcomp, &opt); - if (!NIL_P(opt)) argc--; - check_limit(vstr, opt); - StringValue(vstr); + argc = rb_scan_args(argc, argv, "11:", &vstr, &vcomp, &opt); + vstr = check_limit(vstr, opt); if (!rb_enc_str_asciicompat_p(vstr)) rb_raise(rb_eArgError, "string should have ASCII compatible encoding"); @@ -4507,7 +4697,7 @@ date_s__parse_internal(int argc, VALUE *argv, VALUE klass) * This method recognizes many forms in +string+, * but it is not a validator. * For formats, see - * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc@Specialized+Format+Strings] + * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc@Specialized+Format+Strings] * * If +string+ does not specify a valid date, * the result is unpredictable; @@ -4542,7 +4732,7 @@ date_s__parse(int argc, VALUE *argv, VALUE klass) * This method recognizes many forms in +string+, * but it is not a validator. * For formats, see - * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc@Specialized+Format+Strings] + * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc@Specialized+Format+Strings] * If +string+ does not specify a valid date, * the result is unpredictable; * consider using Date._strptime instead. @@ -4562,7 +4752,7 @@ date_s__parse(int argc, VALUE *argv, VALUE klass) * * See: * - * - Argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * - Argument {limit}[rdoc-ref:Date@Argument+limit]. * * Related: Date._parse (returns a hash). @@ -4572,12 +4762,11 @@ date_s_parse(int argc, VALUE *argv, VALUE klass) { VALUE str, comp, sg, opt; - rb_scan_args(argc, argv, "03:", &str, &comp, &sg, &opt); - if (!NIL_P(opt)) argc--; + argc = rb_scan_args(argc, argv, "03:", &str, &comp, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01"); + str = rb_str_new2(JULIAN_EPOCH_DATE); case 1: comp = Qtrue; case 2: @@ -4607,7 +4796,7 @@ VALUE date__jisx0301(VALUE); * Date._iso8601(string, limit: 128) -> hash * * Returns a hash of values parsed from +string+, which should contain - * an {ISO 8601 formatted date}[rdoc-ref:strftime_formatting.rdoc@ISO+8601+Format+Specifications]: + * an {ISO 8601 formatted date}[rdoc-ref:language/strftime_formatting.rdoc@ISO+8601+Format+Specifications]: * * d = Date.new(2001, 2, 3) * s = d.iso8601 # => "2001-02-03" @@ -4623,7 +4812,7 @@ date_s__iso8601(int argc, VALUE *argv, VALUE klass) VALUE str, opt; rb_scan_args(argc, argv, "1:", &str, &opt); - check_limit(str, opt); + if (!NIL_P(str)) str = check_limit(str, opt); return date__iso8601(str); } @@ -4634,7 +4823,7 @@ date_s__iso8601(int argc, VALUE *argv, VALUE klass) * * Returns a new \Date object with values parsed from +string+, * which should contain - * an {ISO 8601 formatted date}[rdoc-ref:strftime_formatting.rdoc@ISO+8601+Format+Specifications]: + * an {ISO 8601 formatted date}[rdoc-ref:language/strftime_formatting.rdoc@ISO+8601+Format+Specifications]: * * d = Date.new(2001, 2, 3) * s = d.iso8601 # => "2001-02-03" @@ -4642,7 +4831,7 @@ date_s__iso8601(int argc, VALUE *argv, VALUE klass) * * See: * - * - Argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * - Argument {limit}[rdoc-ref:Date@Argument+limit]. * * Related: Date._iso8601 (returns a hash). @@ -4652,12 +4841,11 @@ date_s_iso8601(int argc, VALUE *argv, VALUE klass) { VALUE str, sg, opt; - rb_scan_args(argc, argv, "02:", &str, &sg, &opt); - if (!NIL_P(opt)) argc--; + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01"); + str = rb_str_new2(JULIAN_EPOCH_DATE); case 1: sg = INT2FIX(DEFAULT_SG); } @@ -4677,7 +4865,7 @@ date_s_iso8601(int argc, VALUE *argv, VALUE klass) * Date._rfc3339(string, limit: 128) -> hash * * Returns a hash of values parsed from +string+, which should be a valid - * {RFC 3339 format}[rdoc-ref:strftime_formatting.rdoc@RFC+3339+Format]: + * {RFC 3339 format}[rdoc-ref:language/strftime_formatting.rdoc@RFC+3339+Format]: * * d = Date.new(2001, 2, 3) * s = d.rfc3339 # => "2001-02-03T00:00:00+00:00" @@ -4694,7 +4882,7 @@ date_s__rfc3339(int argc, VALUE *argv, VALUE klass) VALUE str, opt; rb_scan_args(argc, argv, "1:", &str, &opt); - check_limit(str, opt); + if (!NIL_P(str)) str = check_limit(str, opt); return date__rfc3339(str); } @@ -4705,7 +4893,7 @@ date_s__rfc3339(int argc, VALUE *argv, VALUE klass) * * Returns a new \Date object with values parsed from +string+, * which should be a valid - * {RFC 3339 format}[rdoc-ref:strftime_formatting.rdoc@RFC+3339+Format]: + * {RFC 3339 format}[rdoc-ref:language/strftime_formatting.rdoc@RFC+3339+Format]: * * d = Date.new(2001, 2, 3) * s = d.rfc3339 # => "2001-02-03T00:00:00+00:00" @@ -4713,7 +4901,7 @@ date_s__rfc3339(int argc, VALUE *argv, VALUE klass) * * See: * - * - Argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * - Argument {limit}[rdoc-ref:Date@Argument+limit]. * * Related: Date._rfc3339 (returns a hash). @@ -4723,12 +4911,11 @@ date_s_rfc3339(int argc, VALUE *argv, VALUE klass) { VALUE str, sg, opt; - rb_scan_args(argc, argv, "02:", &str, &sg, &opt); - if (!NIL_P(opt)) argc--; + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME); case 1: sg = INT2FIX(DEFAULT_SG); } @@ -4764,7 +4951,7 @@ date_s__xmlschema(int argc, VALUE *argv, VALUE klass) VALUE str, opt; rb_scan_args(argc, argv, "1:", &str, &opt); - check_limit(str, opt); + if (!NIL_P(str)) str = check_limit(str, opt); return date__xmlschema(str); } @@ -4782,7 +4969,7 @@ date_s__xmlschema(int argc, VALUE *argv, VALUE klass) * * See: * - * - Argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * - Argument {limit}[rdoc-ref:Date@Argument+limit]. * * Related: Date._xmlschema (returns a hash). @@ -4792,12 +4979,11 @@ date_s_xmlschema(int argc, VALUE *argv, VALUE klass) { VALUE str, sg, opt; - rb_scan_args(argc, argv, "02:", &str, &sg, &opt); - if (!NIL_P(opt)) argc--; + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01"); + str = rb_str_new2(JULIAN_EPOCH_DATE); case 1: sg = INT2FIX(DEFAULT_SG); } @@ -4817,7 +5003,7 @@ date_s_xmlschema(int argc, VALUE *argv, VALUE klass) * Date._rfc2822(string, limit: 128) -> hash * * Returns a hash of values parsed from +string+, which should be a valid - * {RFC 2822 date format}[rdoc-ref:strftime_formatting.rdoc@RFC+2822+Format]: + * {RFC 2822 date format}[rdoc-ref:language/strftime_formatting.rdoc@RFC+2822+Format]: * * d = Date.new(2001, 2, 3) * s = d.rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000" @@ -4834,7 +5020,7 @@ date_s__rfc2822(int argc, VALUE *argv, VALUE klass) VALUE str, opt; rb_scan_args(argc, argv, "1:", &str, &opt); - check_limit(str, opt); + if (!NIL_P(str)) str = check_limit(str, opt); return date__rfc2822(str); } @@ -4845,7 +5031,7 @@ date_s__rfc2822(int argc, VALUE *argv, VALUE klass) * * Returns a new \Date object with values parsed from +string+, * which should be a valid - * {RFC 2822 date format}[rdoc-ref:strftime_formatting.rdoc@RFC+2822+Format]: + * {RFC 2822 date format}[rdoc-ref:language/strftime_formatting.rdoc@RFC+2822+Format]: * * d = Date.new(2001, 2, 3) * s = d.rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000" @@ -4853,7 +5039,7 @@ date_s__rfc2822(int argc, VALUE *argv, VALUE klass) * * See: * - * - Argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * - Argument {limit}[rdoc-ref:Date@Argument+limit]. * * Related: Date._rfc2822 (returns a hash). @@ -4863,11 +5049,11 @@ date_s_rfc2822(int argc, VALUE *argv, VALUE klass) { VALUE str, sg, opt; - rb_scan_args(argc, argv, "02:", &str, &sg, &opt); + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("Mon, 1 Jan -4712 00:00:00 +0000"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME_RFC3339); case 1: sg = INT2FIX(DEFAULT_SG); } @@ -4887,7 +5073,7 @@ date_s_rfc2822(int argc, VALUE *argv, VALUE klass) * Date._httpdate(string, limit: 128) -> hash * * Returns a hash of values parsed from +string+, which should be a valid - * {HTTP date format}[rdoc-ref:strftime_formatting.rdoc@HTTP+Format]: + * {HTTP date format}[rdoc-ref:language/strftime_formatting.rdoc@HTTP+Format]: * * d = Date.new(2001, 2, 3) * s = d.httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT" @@ -4902,7 +5088,7 @@ date_s__httpdate(int argc, VALUE *argv, VALUE klass) VALUE str, opt; rb_scan_args(argc, argv, "1:", &str, &opt); - check_limit(str, opt); + if (!NIL_P(str)) str = check_limit(str, opt); return date__httpdate(str); } @@ -4913,7 +5099,7 @@ date_s__httpdate(int argc, VALUE *argv, VALUE klass) * * Returns a new \Date object with values parsed from +string+, * which should be a valid - * {HTTP date format}[rdoc-ref:strftime_formatting.rdoc@HTTP+Format]: + * {HTTP date format}[rdoc-ref:language/strftime_formatting.rdoc@HTTP+Format]: * * d = Date.new(2001, 2, 3) s = d.httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT" @@ -4921,7 +5107,7 @@ date_s__httpdate(int argc, VALUE *argv, VALUE klass) * * See: * - * - Argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * - Argument {limit}[rdoc-ref:Date@Argument+limit]. * * Related: Date._httpdate (returns a hash). @@ -4931,11 +5117,11 @@ date_s_httpdate(int argc, VALUE *argv, VALUE klass) { VALUE str, sg, opt; - rb_scan_args(argc, argv, "02:", &str, &sg, &opt); + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("Mon, 01 Jan -4712 00:00:00 GMT"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME_HTTPDATE); case 1: sg = INT2FIX(DEFAULT_SG); } @@ -4955,7 +5141,7 @@ date_s_httpdate(int argc, VALUE *argv, VALUE klass) * Date._jisx0301(string, limit: 128) -> hash * * Returns a hash of values parsed from +string+, which should be a valid - * {JIS X 0301 date format}[rdoc-ref:strftime_formatting.rdoc@JIS+X+0301+Format]: + * {JIS X 0301 date format}[rdoc-ref:language/strftime_formatting.rdoc@JIS+X+0301+Format]: * * d = Date.new(2001, 2, 3) * s = d.jisx0301 # => "H13.02.03" @@ -4971,7 +5157,7 @@ date_s__jisx0301(int argc, VALUE *argv, VALUE klass) VALUE str, opt; rb_scan_args(argc, argv, "1:", &str, &opt); - check_limit(str, opt); + if (!NIL_P(str)) str = check_limit(str, opt); return date__jisx0301(str); } @@ -4981,7 +5167,7 @@ date_s__jisx0301(int argc, VALUE *argv, VALUE klass) * Date.jisx0301(string = '-4712-01-01', start = Date::ITALY, limit: 128) -> date * * Returns a new \Date object with values parsed from +string+, - * which should be a valid {JIS X 0301 format}[rdoc-ref:strftime_formatting.rdoc@JIS+X+0301+Format]: + * which should be a valid {JIS X 0301 format}[rdoc-ref:language/strftime_formatting.rdoc@JIS+X+0301+Format]: * * d = Date.new(2001, 2, 3) * s = d.jisx0301 # => "H13.02.03" @@ -4993,7 +5179,7 @@ date_s__jisx0301(int argc, VALUE *argv, VALUE klass) * * See: * - * - Argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * - Argument {limit}[rdoc-ref:Date@Argument+limit]. * * Related: Date._jisx0301 (returns a hash). @@ -5003,12 +5189,11 @@ date_s_jisx0301(int argc, VALUE *argv, VALUE klass) { VALUE str, sg, opt; - rb_scan_args(argc, argv, "02:", &str, &sg, &opt); - if (!NIL_P(opt)) argc--; + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01"); + str = rb_str_new2(JULIAN_EPOCH_DATE); case 1: sg = INT2FIX(DEFAULT_SG); } @@ -5752,7 +5937,7 @@ d_lite_leap_p(VALUE self) * Date.new(2001, 2, 3, Date::GREGORIAN).start # => -Infinity * Date.new(2001, 2, 3, Date::JULIAN).start # => Infinity * - * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * */ static VALUE @@ -5827,7 +6012,7 @@ dup_obj_with_new_start(VALUE obj, double sg) * d1 = d0.new_start(Date::JULIAN) * d1.julian? # => true * - * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * */ static VALUE @@ -6332,9 +6517,11 @@ minus_dd(VALUE self, VALUE other) * call-seq: * d - other -> date or rational * - * Returns the difference between the two dates if the other is a date - * object. If the other is a numeric value, returns a date object - * pointing +other+ days before self. If the other is a fractional number, + * If the other is a date object, returns a Rational + * whose value is the difference between the two dates in days. + * If the other is a numeric value, returns a date object + * pointing +other+ days before self. + * If the other is a fractional number, * assumes its precision is at most nanosecond. * * Date.new(2001,2,3) - 1 #=> #<Date: 2001-02-02 ...> @@ -6941,13 +7128,24 @@ d_lite_eql_p(VALUE self, VALUE other) static VALUE d_lite_hash(VALUE self) { - st_index_t v, h[4]; + st_index_t v, h[5]; + VALUE nth; get_d1(self); - h[0] = m_nth(dat); - h[1] = m_jd(dat); - h[2] = m_df(dat); - h[3] = m_sf(dat); + nth = m_nth(dat); + + if (FIXNUM_P(nth)) { + h[0] = 0; + h[1] = (st_index_t)nth; + } else { + h[0] = 1; + h[1] = (st_index_t)FIX2LONG(rb_hash(nth)); + } + + h[2] = m_jd(dat); + h[3] = m_df(dat); + h[4] = m_sf(dat); + v = rb_memhash(h, sizeof(h)); return ST2FIX(v); } @@ -6962,7 +7160,7 @@ static VALUE strftimev(const char *, VALUE, * to_s -> string * * Returns a string representation of the date in +self+ - * in {ISO 8601 extended date format}[rdoc-ref:strftime_formatting.rdoc@ISO+8601+Format+Specifications] + * in {ISO 8601 extended date format}[rdoc-ref:language/strftime_formatting.rdoc@ISO+8601+Format+Specifications] * (<tt>'%Y-%m-%d'</tt>): * * Date.new(2001, 2, 3).to_s # => "2001-02-03" @@ -7243,7 +7441,7 @@ date_strftime_internal(int argc, VALUE *argv, VALUE self, * Date.new(2001, 2, 3).strftime # => "2001-02-03" * * For other formats, see - * {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]. + * {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]. * */ static VALUE @@ -7275,7 +7473,7 @@ strftimev(const char *fmt, VALUE self, * asctime -> string * * Equivalent to #strftime with argument <tt>'%a %b %e %T %Y'</tt> - * (or its {shorthand form}[rdoc-ref:strftime_formatting.rdoc@Shorthand+Conversion+Specifiers] + * (or its {shorthand form}[rdoc-ref:language/strftime_formatting.rdoc@Shorthand+Conversion+Specifiers] * <tt>'%c'</tt>): * * Date.new(2001, 2, 3).asctime # => "Sat Feb 3 00:00:00 2001" @@ -7294,7 +7492,7 @@ d_lite_asctime(VALUE self) * iso8601 -> string * * Equivalent to #strftime with argument <tt>'%Y-%m-%d'</tt> - * (or its {shorthand form}[rdoc-ref:strftime_formatting.rdoc@Shorthand+Conversion+Specifiers] + * (or its {shorthand form}[rdoc-ref:language/strftime_formatting.rdoc@Shorthand+Conversion+Specifiers] * <tt>'%F'</tt>); * * Date.new(2001, 2, 3).iso8601 # => "2001-02-03" @@ -7311,7 +7509,7 @@ d_lite_iso8601(VALUE self) * rfc3339 -> string * * Equivalent to #strftime with argument <tt>'%FT%T%:z'</tt>; - * see {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]: + * see {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]: * * Date.new(2001, 2, 3).rfc3339 # => "2001-02-03T00:00:00+00:00" * @@ -7327,7 +7525,7 @@ d_lite_rfc3339(VALUE self) * rfc2822 -> string * * Equivalent to #strftime with argument <tt>'%a, %-d %b %Y %T %z'</tt>; - * see {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]: + * see {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]: * * Date.new(2001, 2, 3).rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000" * @@ -7343,7 +7541,7 @@ d_lite_rfc2822(VALUE self) * httpdate -> string * * Equivalent to #strftime with argument <tt>'%a, %d %b %Y %T GMT'</tt>; - * see {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]: + * see {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]: * * Date.new(2001, 2, 3).httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT" * @@ -7522,10 +7720,7 @@ d_lite_marshal_dump_old(VALUE self) m_of_in_day(dat), DBL2NUM(m_sg(dat))); - if (FL_TEST(self, FL_EXIVAR)) { - rb_copy_generic_ivar(a, self); - FL_SET(a, FL_EXIVAR); - } + rb_copy_generic_ivar(a, self); return a; } @@ -7547,10 +7742,8 @@ d_lite_marshal_dump(VALUE self) INT2FIX(m_of(dat)), DBL2NUM(m_sg(dat))); - if (FL_TEST(self, FL_EXIVAR)) { - rb_copy_generic_ivar(a, self); - FL_SET(a, FL_EXIVAR); - } + + rb_copy_generic_ivar(a, self); return a; } @@ -7623,10 +7816,7 @@ d_lite_marshal_load(VALUE self, VALUE a) HAVE_JD | HAVE_DF); } - if (FL_TEST(a, FL_EXIVAR)) { - rb_copy_generic_ivar(self, a); - FL_SET(self, FL_EXIVAR); - } + rb_copy_generic_ivar(self, a); return self; } @@ -8379,7 +8569,7 @@ datetime_s_strptime(int argc, VALUE *argv, VALUE klass) switch (argc) { case 0: - str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME); case 1: fmt = rb_str_new2("%FT%T%z"); case 2: @@ -8427,12 +8617,11 @@ datetime_s_parse(int argc, VALUE *argv, VALUE klass) { VALUE str, comp, sg, opt; - rb_scan_args(argc, argv, "03:", &str, &comp, &sg, &opt); - if (!NIL_P(opt)) argc--; + argc = rb_scan_args(argc, argv, "03:", &str, &comp, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME); case 1: comp = Qtrue; case 2: @@ -8474,12 +8663,11 @@ datetime_s_iso8601(int argc, VALUE *argv, VALUE klass) { VALUE str, sg, opt; - rb_scan_args(argc, argv, "02:", &str, &sg, &opt); - if (!NIL_P(opt)) argc--; + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME); case 1: sg = INT2FIX(DEFAULT_SG); } @@ -8489,7 +8677,7 @@ datetime_s_iso8601(int argc, VALUE *argv, VALUE klass) VALUE argv2[2], hash; argv2[0] = str; argv2[1] = opt; - if (!NIL_P(opt)) argc2--; + if (!NIL_P(opt)) argc2++; hash = date_s__iso8601(argc2, argv2, klass); return dt_new_by_frags(klass, hash, sg); } @@ -8514,12 +8702,11 @@ datetime_s_rfc3339(int argc, VALUE *argv, VALUE klass) { VALUE str, sg, opt; - rb_scan_args(argc, argv, "02:", &str, &sg, &opt); - if (!NIL_P(opt)) argc--; + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME); case 1: sg = INT2FIX(DEFAULT_SG); } @@ -8554,12 +8741,11 @@ datetime_s_xmlschema(int argc, VALUE *argv, VALUE klass) { VALUE str, sg, opt; - rb_scan_args(argc, argv, "02:", &str, &sg, &opt); - if (!NIL_P(opt)) argc--; + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME); case 1: sg = INT2FIX(DEFAULT_SG); } @@ -8595,12 +8781,11 @@ datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass) { VALUE str, sg, opt; - rb_scan_args(argc, argv, "02:", &str, &sg, &opt); - if (!NIL_P(opt)) argc--; + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("Mon, 1 Jan -4712 00:00:00 +0000"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME_RFC3339); case 1: sg = INT2FIX(DEFAULT_SG); } @@ -8635,12 +8820,11 @@ datetime_s_httpdate(int argc, VALUE *argv, VALUE klass) { VALUE str, sg, opt; - rb_scan_args(argc, argv, "02:", &str, &sg, &opt); - if (!NIL_P(opt)) argc--; + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("Mon, 01 Jan -4712 00:00:00 GMT"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME_HTTPDATE); case 1: sg = INT2FIX(DEFAULT_SG); } @@ -8680,12 +8864,11 @@ datetime_s_jisx0301(int argc, VALUE *argv, VALUE klass) { VALUE str, sg, opt; - rb_scan_args(argc, argv, "02:", &str, &sg, &opt); - if (!NIL_P(opt)) argc--; + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME); case 1: sg = INT2FIX(DEFAULT_SG); } @@ -8727,7 +8910,7 @@ dt_lite_to_s(VALUE self) * DateTime.now.strftime # => "2022-07-01T11:03:19-05:00" * * For other formats, - * see {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]: + * see {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]: * */ static VALUE @@ -8961,18 +9144,23 @@ time_to_datetime(VALUE self) static VALUE date_to_time(VALUE self) { + VALUE t; + get_d1a(self); if (m_julian_p(adat)) { - VALUE tmp = d_lite_gregorian(self); - get_d1b(tmp); + VALUE g = d_lite_gregorian(self); + get_d1b(g); adat = bdat; + self = g; } - return f_local3(rb_cTime, + t = f_local3(rb_cTime, m_real_year(adat), INT2FIX(m_mon(adat)), INT2FIX(m_mday(adat))); + RB_GC_GUARD(self); /* may be the converted gregorian */ + return t; } /* @@ -9042,9 +9230,10 @@ datetime_to_time(VALUE self) get_d1(self); if (m_julian_p(dat)) { - self = d_lite_gregorian(self); - get_d1a(self); + VALUE g = d_lite_gregorian(self); + get_d1a(g); dat = adat; + self = g; } { @@ -9061,6 +9250,7 @@ datetime_to_time(VALUE self) f_add(INT2FIX(m_sec(dat)), m_sf_in_sec(dat)), INT2FIX(m_of(dat))); + RB_GC_GUARD(self); /* may be the converted gregorian */ return t; } } @@ -9465,7 +9655,7 @@ mk_ary_of_str(long len, const char *a[]) } rb_ary_push(o, e); } - rb_obj_freeze(o); + rb_ary_freeze(o); return o; } @@ -9499,6 +9689,7 @@ Init_date_core(void) sym_zone = ID2SYM(rb_intern_const("zone")); half_days_in_day = rb_rational_new2(INT2FIX(1), INT2FIX(2)); + rb_gc_register_mark_object(half_days_in_day); #if (LONG_MAX / DAY_IN_SECONDS) > SECOND_IN_NANOSECONDS day_in_nanoseconds = LONG2NUM((long)DAY_IN_SECONDS * @@ -9510,8 +9701,6 @@ Init_date_core(void) day_in_nanoseconds = f_mul(INT2FIX(DAY_IN_SECONDS), INT2FIX(SECOND_IN_NANOSECONDS)); #endif - - rb_gc_register_mark_object(half_days_in_day); rb_gc_register_mark_object(day_in_nanoseconds); positive_inf = +INFINITY; @@ -9527,7 +9716,7 @@ Init_date_core(void) * * - You need both dates and times; \Date handles only dates. * - You need only Gregorian dates (and not Julian dates); - * see {Julian and Gregorian Calendars}[rdoc-ref:calendars.rdoc]. + * see {Julian and Gregorian Calendars}[rdoc-ref:language/calendars.rdoc]. * * A \Date object, once created, is immutable, and cannot be modified. * @@ -9574,7 +9763,7 @@ Init_date_core(void) * Date.strptime('fri31dec99', '%a%d%b%y') # => #<Date: 1999-12-31> * * See also the specialized methods in - * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc@Specialized+Format+Strings] + * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc@Specialized+Format+Strings] * * == Argument +limit+ * diff --git a/ext/date/date_parse.c b/ext/date/date_parse.c index c6f26ecb91..a1600e4708 100644 --- a/ext/date/date_parse.c +++ b/ext/date/date_parse.c @@ -7,6 +7,9 @@ #include "ruby/re.h" #include <ctype.h> +#undef strncasecmp +#define strncasecmp STRNCASECMP + RUBY_EXTERN VALUE rb_int_positive_pow(long x, unsigned long y); RUBY_EXTERN unsigned long ruby_scan_digits(const char *str, ssize_t len, int base, size_t *retlen, int *overflow); diff --git a/ext/date/date_strptime.c b/ext/date/date_strptime.c index f731629df1..f1c8201de8 100644 --- a/ext/date/date_strptime.c +++ b/ext/date/date_strptime.c @@ -7,6 +7,9 @@ #include "ruby/re.h" #include <ctype.h> +#undef strncasecmp +#define strncasecmp STRNCASECMP + static const char *day_names[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", @@ -119,8 +122,9 @@ do { \ do { \ size_t l; \ l = read_digits(&str[si], slen - si, &n, w); \ - if (l == 0) \ + if (l == 0) { \ fail(); \ + } \ si += l; \ } while (0) diff --git a/ext/date/depend b/ext/date/depend index 82f85f7bf3..4fb78149a1 100644 --- a/ext/date/depend +++ b/ext/date/depend @@ -138,6 +138,7 @@ date_core.o: $(hdrdir)/ruby/internal/intern/re.h date_core.o: $(hdrdir)/ruby/internal/intern/ruby.h date_core.o: $(hdrdir)/ruby/internal/intern/select.h date_core.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +date_core.o: $(hdrdir)/ruby/internal/intern/set.h date_core.o: $(hdrdir)/ruby/internal/intern/signal.h date_core.o: $(hdrdir)/ruby/internal/intern/sprintf.h date_core.o: $(hdrdir)/ruby/internal/intern/string.h @@ -157,6 +158,7 @@ date_core.o: $(hdrdir)/ruby/internal/special_consts.h date_core.o: $(hdrdir)/ruby/internal/static_assert.h date_core.o: $(hdrdir)/ruby/internal/stdalign.h date_core.o: $(hdrdir)/ruby/internal/stdbool.h +date_core.o: $(hdrdir)/ruby/internal/stdckdint.h date_core.o: $(hdrdir)/ruby/internal/symbol.h date_core.o: $(hdrdir)/ruby/internal/value.h date_core.o: $(hdrdir)/ruby/internal/value_type.h @@ -312,6 +314,7 @@ date_parse.o: $(hdrdir)/ruby/internal/intern/re.h date_parse.o: $(hdrdir)/ruby/internal/intern/ruby.h date_parse.o: $(hdrdir)/ruby/internal/intern/select.h date_parse.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +date_parse.o: $(hdrdir)/ruby/internal/intern/set.h date_parse.o: $(hdrdir)/ruby/internal/intern/signal.h date_parse.o: $(hdrdir)/ruby/internal/intern/sprintf.h date_parse.o: $(hdrdir)/ruby/internal/intern/string.h @@ -331,6 +334,7 @@ date_parse.o: $(hdrdir)/ruby/internal/special_consts.h date_parse.o: $(hdrdir)/ruby/internal/static_assert.h date_parse.o: $(hdrdir)/ruby/internal/stdalign.h date_parse.o: $(hdrdir)/ruby/internal/stdbool.h +date_parse.o: $(hdrdir)/ruby/internal/stdckdint.h date_parse.o: $(hdrdir)/ruby/internal/symbol.h date_parse.o: $(hdrdir)/ruby/internal/value.h date_parse.o: $(hdrdir)/ruby/internal/value_type.h @@ -476,6 +480,7 @@ date_strftime.o: $(hdrdir)/ruby/internal/intern/re.h date_strftime.o: $(hdrdir)/ruby/internal/intern/ruby.h date_strftime.o: $(hdrdir)/ruby/internal/intern/select.h date_strftime.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +date_strftime.o: $(hdrdir)/ruby/internal/intern/set.h date_strftime.o: $(hdrdir)/ruby/internal/intern/signal.h date_strftime.o: $(hdrdir)/ruby/internal/intern/sprintf.h date_strftime.o: $(hdrdir)/ruby/internal/intern/string.h @@ -495,6 +500,7 @@ date_strftime.o: $(hdrdir)/ruby/internal/special_consts.h date_strftime.o: $(hdrdir)/ruby/internal/static_assert.h date_strftime.o: $(hdrdir)/ruby/internal/stdalign.h date_strftime.o: $(hdrdir)/ruby/internal/stdbool.h +date_strftime.o: $(hdrdir)/ruby/internal/stdckdint.h date_strftime.o: $(hdrdir)/ruby/internal/symbol.h date_strftime.o: $(hdrdir)/ruby/internal/value.h date_strftime.o: $(hdrdir)/ruby/internal/value_type.h @@ -647,6 +653,7 @@ date_strptime.o: $(hdrdir)/ruby/internal/intern/re.h date_strptime.o: $(hdrdir)/ruby/internal/intern/ruby.h date_strptime.o: $(hdrdir)/ruby/internal/intern/select.h date_strptime.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +date_strptime.o: $(hdrdir)/ruby/internal/intern/set.h date_strptime.o: $(hdrdir)/ruby/internal/intern/signal.h date_strptime.o: $(hdrdir)/ruby/internal/intern/sprintf.h date_strptime.o: $(hdrdir)/ruby/internal/intern/string.h @@ -666,6 +673,7 @@ date_strptime.o: $(hdrdir)/ruby/internal/special_consts.h date_strptime.o: $(hdrdir)/ruby/internal/static_assert.h date_strptime.o: $(hdrdir)/ruby/internal/stdalign.h date_strptime.o: $(hdrdir)/ruby/internal/stdbool.h +date_strptime.o: $(hdrdir)/ruby/internal/stdckdint.h date_strptime.o: $(hdrdir)/ruby/internal/symbol.h date_strptime.o: $(hdrdir)/ruby/internal/value.h date_strptime.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/date/extconf.rb b/ext/date/extconf.rb index 358f64173a..8a1467df09 100644 --- a/ext/date/extconf.rb +++ b/ext/date/extconf.rb @@ -3,6 +3,7 @@ require 'mkmf' config_string("strict_warnflags") {|w| $warnflags += " #{w}"} +append_cflags("-Wno-compound-token-split-by-macro") if RUBY_VERSION < "2.7." have_func("rb_category_warn") with_werror("", {:werror => true}) do |opt, | have_var("timezone", "time.h", opt) diff --git a/ext/date/lib/date.rb b/ext/date/lib/date.rb index 6888bd2d4e..0cb763017f 100644 --- a/ext/date/lib/date.rb +++ b/ext/date/lib/date.rb @@ -4,7 +4,7 @@ require 'date_core' class Date - VERSION = "3.3.4" # :nodoc: + VERSION = "3.5.1" # :nodoc: # call-seq: # infinite? -> false diff --git a/ext/date/prereq.mk b/ext/date/prereq.mk index cee7685975..b5d271a32c 100644 --- a/ext/date/prereq.mk +++ b/ext/date/prereq.mk @@ -1,7 +1,7 @@ .SUFFIXES: .list .list.h: - gperf --ignore-case -C -c -P -p -j1 -i 1 -g -o -t -N $(*F) $< \ + gperf --ignore-case -L ANSI-C -C -c -P -p -j1 -i 1 -g -o -t -N $(*F) $< \ | sed -f $(top_srcdir)/tool/gperf.sed \ > $(@F) diff --git a/ext/date/zonetab.h b/ext/date/zonetab.h index 7ced9e0308..2a2e8910c9 100644 --- a/ext/date/zonetab.h +++ b/ext/date/zonetab.h @@ -1,5 +1,5 @@ /* ANSI-C code produced by gperf version 3.1 */ -/* Command-line: gperf --ignore-case -C -c -P -p -j1 -i 1 -g -o -t -N zonetab zonetab.list */ +/* Command-line: gperf --ignore-case -L ANSI-C -C -c -P -p -j1 -i 1 -g -o -t -N zonetab zonetab.list */ /* Computed positions: -k'1-4,9' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ @@ -29,15 +29,17 @@ #error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>." #endif -#define gperf_offsetof(s, n) (short)offsetof(struct s##_t, s##_str##n) #line 1 "zonetab.list" +#define GPERF_DOWNCASE 1 +#define GPERF_CASE_STRNCMP 1 +#define gperf_case_strncmp strncasecmp struct zone { int name; int offset; }; static const struct zone *zonetab(register const char *str, register size_t len); -#line 9 "zonetab.list" +#line 12 "zonetab.list" struct zone; #define TOTAL_KEYWORDS 316 @@ -49,7 +51,7 @@ struct zone; #ifndef GPERF_DOWNCASE #define GPERF_DOWNCASE 1 -static const unsigned char gperf_downcase[256] = +static unsigned char gperf_downcase[256] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, @@ -808,736 +810,736 @@ zonetab (register const char *str, register size_t len) static const struct zone wordlist[] = { {-1}, {-1}, -#line 34 "zonetab.list" - {gperf_offsetof(stringpool, 2), -2*3600}, -#line 43 "zonetab.list" - {gperf_offsetof(stringpool, 3), -11*3600}, -#line 45 "zonetab.list" - {gperf_offsetof(stringpool, 4), 0*3600}, -#line 36 "zonetab.list" - {gperf_offsetof(stringpool, 5), -4*3600}, - {-1}, {-1}, -#line 269 "zonetab.list" - {gperf_offsetof(stringpool, 8),21600}, -#line 268 "zonetab.list" - {gperf_offsetof(stringpool, 9),25200}, -#line 35 "zonetab.list" - {gperf_offsetof(stringpool, 10), -3*3600}, +#line 37 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2, -2*3600}, +#line 46 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str3, -11*3600}, +#line 48 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str4, 0*3600}, +#line 39 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str5, -4*3600}, {-1}, {-1}, -#line 21 "zonetab.list" - {gperf_offsetof(stringpool, 13), 1*3600}, -#line 25 "zonetab.list" - {gperf_offsetof(stringpool, 14), 5*3600}, -#line 271 "zonetab.list" - {gperf_offsetof(stringpool, 15),-18000}, -#line 279 "zonetab.list" - {gperf_offsetof(stringpool, 16),-10800}, -#line 273 "zonetab.list" - {gperf_offsetof(stringpool, 17),43200}, #line 272 "zonetab.list" - {gperf_offsetof(stringpool, 18),43200}, -#line 80 "zonetab.list" - {gperf_offsetof(stringpool, 19), 2*3600}, -#line 186 "zonetab.list" - {gperf_offsetof(stringpool, 20),36000}, -#line 88 "zonetab.list" - {gperf_offsetof(stringpool, 21), 3*3600}, -#line 87 "zonetab.list" - {gperf_offsetof(stringpool, 22), 3*3600}, - {-1}, -#line 101 "zonetab.list" - {gperf_offsetof(stringpool, 24),-6*3600}, -#line 217 "zonetab.list" - {gperf_offsetof(stringpool, 25),-18000}, -#line 19 "zonetab.list" - {gperf_offsetof(stringpool, 26), -8*3600}, -#line 133 "zonetab.list" - {gperf_offsetof(stringpool, 27), -18000}, -#line 32 "zonetab.list" - {gperf_offsetof(stringpool, 28), 12*3600}, -#line 56 "zonetab.list" - {gperf_offsetof(stringpool, 29), -4*3600}, -#line 13 "zonetab.list" - {gperf_offsetof(stringpool, 30), -5*3600}, -#line 23 "zonetab.list" - {gperf_offsetof(stringpool, 31), 3*3600}, -#line 256 "zonetab.list" - {gperf_offsetof(stringpool, 32),23400}, -#line 73 "zonetab.list" - {gperf_offsetof(stringpool, 33), 1*3600}, - {-1}, -#line 82 "zonetab.list" - {gperf_offsetof(stringpool, 35), 2*3600}, -#line 71 "zonetab.list" - {gperf_offsetof(stringpool, 36), 1*3600}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str8,21600}, +#line 271 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str9,25200}, +#line 38 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str10, -3*3600}, + {-1}, {-1}, #line 24 "zonetab.list" - {gperf_offsetof(stringpool, 37), 4*3600}, -#line 79 "zonetab.list" - {gperf_offsetof(stringpool, 38), 2*3600}, -#line 65 "zonetab.list" - {gperf_offsetof(stringpool, 39),2*3600}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str13, 1*3600}, +#line 28 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str14, 5*3600}, +#line 274 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str15,-18000}, +#line 282 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str16,-10800}, +#line 276 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str17,43200}, +#line 275 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str18,43200}, +#line 83 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str19, 2*3600}, +#line 189 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str20,36000}, +#line 91 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str21, 3*3600}, +#line 90 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str22, 3*3600}, {-1}, -#line 202 "zonetab.list" - {gperf_offsetof(stringpool, 41),28800}, -#line 252 "zonetab.list" - {gperf_offsetof(stringpool, 42),39600}, -#line 251 "zonetab.list" - {gperf_offsetof(stringpool, 43),43200}, -#line 17 "zonetab.list" - {gperf_offsetof(stringpool, 44), -7*3600}, -#line 89 "zonetab.list" - {gperf_offsetof(stringpool, 45), 3*3600}, -#line 212 "zonetab.list" - {gperf_offsetof(stringpool, 46),-18000}, -#line 15 "zonetab.list" - {gperf_offsetof(stringpool, 47), -6*3600}, -#line 192 "zonetab.list" - {gperf_offsetof(stringpool, 48),18000}, -#line 26 "zonetab.list" - {gperf_offsetof(stringpool, 49), 6*3600}, - {-1}, {-1}, -#line 51 "zonetab.list" - {gperf_offsetof(stringpool, 52), -3*3600}, -#line 226 "zonetab.list" - {gperf_offsetof(stringpool, 53),-7200}, -#line 221 "zonetab.list" - {gperf_offsetof(stringpool, 54),10800}, +#line 104 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str24,-6*3600}, +#line 220 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str25,-18000}, #line 22 "zonetab.list" - {gperf_offsetof(stringpool, 55), 2*3600}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str26, -8*3600}, +#line 136 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str27, -18000}, +#line 35 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str28, 12*3600}, +#line 59 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str29, -4*3600}, +#line 16 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str30, -5*3600}, +#line 26 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str31, 3*3600}, +#line 259 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str32,23400}, +#line 76 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str33, 1*3600}, {-1}, -#line 190 "zonetab.list" - {gperf_offsetof(stringpool, 57),43200}, -#line 189 "zonetab.list" - {gperf_offsetof(stringpool, 58),43200}, -#line 199 "zonetab.list" - {gperf_offsetof(stringpool, 59),28800}, -#line 29 "zonetab.list" - {gperf_offsetof(stringpool, 60), 9*3600}, -#line 276 "zonetab.list" - {gperf_offsetof(stringpool, 61),28800}, -#line 48 "zonetab.list" - {gperf_offsetof(stringpool, 62), -2*3600}, -#line 94 "zonetab.list" - {gperf_offsetof(stringpool, 63), 6*3600}, +#line 85 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str35, 2*3600}, #line 74 "zonetab.list" - {gperf_offsetof(stringpool, 64), 1*3600}, -#line 81 "zonetab.list" - {gperf_offsetof(stringpool, 65), 2*3600}, -#line 64 "zonetab.list" - {gperf_offsetof(stringpool, 66),-10*3600}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str36, 1*3600}, +#line 27 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str37, 4*3600}, +#line 82 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str38, 2*3600}, +#line 68 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str39,2*3600}, + {-1}, +#line 205 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str41,28800}, +#line 255 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str42,39600}, #line 254 "zonetab.list" - {gperf_offsetof(stringpool, 67),18000}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str43,43200}, +#line 20 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str44, -7*3600}, #line 92 "zonetab.list" - {gperf_offsetof(stringpool, 68), 5*3600}, - {-1}, -#line 200 "zonetab.list" - {gperf_offsetof(stringpool, 70),-14400}, -#line 70 "zonetab.list" - {gperf_offsetof(stringpool, 71), 1*3600}, -#line 281 "zonetab.list" - {gperf_offsetof(stringpool, 72),32400}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str45, 3*3600}, +#line 215 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str46,-18000}, +#line 18 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str47, -6*3600}, +#line 195 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str48,18000}, +#line 29 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str49, 6*3600}, + {-1}, {-1}, +#line 54 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str52, -3*3600}, +#line 229 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str53,-7200}, +#line 224 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str54,10800}, +#line 25 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str55, 2*3600}, {-1}, -#line 280 "zonetab.list" - {gperf_offsetof(stringpool, 74),39600}, -#line 238 "zonetab.list" - {gperf_offsetof(stringpool, 75),21600}, -#line 93 "zonetab.list" - {gperf_offsetof(stringpool, 76), (5*3600+1800)}, -#line 194 "zonetab.list" - {gperf_offsetof(stringpool, 77),28800}, +#line 193 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str57,43200}, +#line 192 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str58,43200}, +#line 202 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str59,28800}, +#line 32 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str60, 9*3600}, +#line 279 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str61,28800}, +#line 51 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str62, -2*3600}, +#line 97 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str63, 6*3600}, +#line 77 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str64, 1*3600}, +#line 84 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str65, 2*3600}, +#line 67 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str66,-10*3600}, +#line 257 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str67,18000}, +#line 95 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str68, 5*3600}, {-1}, -#line 255 "zonetab.list" - {gperf_offsetof(stringpool, 79),43200}, -#line 75 "zonetab.list" - {gperf_offsetof(stringpool, 80), 1*3600}, -#line 270 "zonetab.list" - {gperf_offsetof(stringpool, 81),18000}, -#line 83 "zonetab.list" - {gperf_offsetof(stringpool, 82), 2*3600}, +#line 203 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str70,-14400}, +#line 73 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str71, 1*3600}, +#line 284 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str72,32400}, {-1}, -#line 207 "zonetab.list" - {gperf_offsetof(stringpool, 84),36000}, -#line 278 "zonetab.list" - {gperf_offsetof(stringpool, 85),-7200}, - {-1}, {-1}, -#line 126 "zonetab.list" - {gperf_offsetof(stringpool, 88), -21600}, -#line 185 "zonetab.list" - {gperf_offsetof(stringpool, 89),39600}, -#line 183 "zonetab.list" - {gperf_offsetof(stringpool, 90),-18000}, -#line 218 "zonetab.list" - {gperf_offsetof(stringpool, 91),-18000}, -#line 182 "zonetab.list" - {gperf_offsetof(stringpool, 92),34200}, -#line 103 "zonetab.list" - {gperf_offsetof(stringpool, 93),11*3600}, -#line 53 "zonetab.list" - {gperf_offsetof(stringpool, 94), -3*3600}, -#line 208 "zonetab.list" - {gperf_offsetof(stringpool, 95),36000}, -#line 49 "zonetab.list" - {gperf_offsetof(stringpool, 96),-2*3600}, -#line 120 "zonetab.list" - {gperf_offsetof(stringpool, 97), 34200}, - {-1}, {-1}, -#line 215 "zonetab.list" - {gperf_offsetof(stringpool, 100),25200}, -#line 242 "zonetab.list" - {gperf_offsetof(stringpool, 101),12600}, +#line 283 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str74,39600}, #line 241 "zonetab.list" - {gperf_offsetof(stringpool, 102),28800}, -#line 240 "zonetab.list" - {gperf_offsetof(stringpool, 103),32400}, -#line 86 "zonetab.list" - {gperf_offsetof(stringpool, 104), 3*3600}, -#line 33 "zonetab.list" - {gperf_offsetof(stringpool, 105), -1*3600}, -#line 201 "zonetab.list" - {gperf_offsetof(stringpool, 106),21600}, -#line 148 "zonetab.list" - {gperf_offsetof(stringpool, 107), -25200}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str75,21600}, #line 96 "zonetab.list" - {gperf_offsetof(stringpool, 108), (6*3600+1800)}, -#line 42 "zonetab.list" - {gperf_offsetof(stringpool, 109), -10*3600}, -#line 31 "zonetab.list" - {gperf_offsetof(stringpool, 110), 11*3600}, -#line 72 "zonetab.list" - {gperf_offsetof(stringpool, 111), 1*3600}, - {-1}, -#line 90 "zonetab.list" - {gperf_offsetof(stringpool, 113), 4*3600}, -#line 47 "zonetab.list" - {gperf_offsetof(stringpool, 114), 0*3600}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str76, (5*3600+1800)}, +#line 197 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str77,28800}, {-1}, +#line 258 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str79,43200}, #line 78 "zonetab.list" - {gperf_offsetof(stringpool, 116), 1*3600}, -#line 77 "zonetab.list" - {gperf_offsetof(stringpool, 117), 1*3600}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str80, 1*3600}, +#line 273 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str81,18000}, +#line 86 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str82, 2*3600}, {-1}, -#line 95 "zonetab.list" - {gperf_offsetof(stringpool, 119), 2*3600}, -#line 313 "zonetab.list" - {gperf_offsetof(stringpool, 120),43200}, -#line 55 "zonetab.list" - {gperf_offsetof(stringpool, 121), -(2*3600+1800)}, -#line 184 "zonetab.list" - {gperf_offsetof(stringpool, 122),31500}, -#line 204 "zonetab.list" - {gperf_offsetof(stringpool, 123),45900}, #line 210 "zonetab.list" - {gperf_offsetof(stringpool, 124),-18000}, -#line 198 "zonetab.list" - {gperf_offsetof(stringpool, 125),14400}, -#line 57 "zonetab.list" - {gperf_offsetof(stringpool, 126), -4*3600}, -#line 197 "zonetab.list" - {gperf_offsetof(stringpool, 127),18000}, -#line 54 "zonetab.list" - {gperf_offsetof(stringpool, 128),-3*3600}, -#line 253 "zonetab.list" - {gperf_offsetof(stringpool, 129),-30600}, -#line 91 "zonetab.list" - {gperf_offsetof(stringpool, 130), 4*3600}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str84,36000}, +#line 281 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str85,-7200}, + {-1}, {-1}, +#line 129 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str88, -21600}, +#line 188 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str89,39600}, +#line 186 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str90,-18000}, +#line 221 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str91,-18000}, +#line 185 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str92,34200}, +#line 106 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str93,11*3600}, +#line 56 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str94, -3*3600}, +#line 211 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str95,36000}, +#line 52 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str96,-2*3600}, +#line 123 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str97, 34200}, + {-1}, {-1}, +#line 218 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str100,25200}, +#line 245 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str101,12600}, +#line 244 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str102,28800}, +#line 243 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str103,32400}, +#line 89 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str104, 3*3600}, +#line 36 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str105, -1*3600}, +#line 204 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str106,21600}, +#line 151 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str107, -25200}, #line 99 "zonetab.list" - {gperf_offsetof(stringpool, 131), 9*3600}, -#line 122 "zonetab.list" - {gperf_offsetof(stringpool, 132), 21600}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str108, (6*3600+1800)}, +#line 45 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str109, -10*3600}, +#line 34 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str110, 11*3600}, +#line 75 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str111, 1*3600}, + {-1}, +#line 93 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str113, 4*3600}, +#line 50 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str114, 0*3600}, + {-1}, +#line 81 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str116, 1*3600}, +#line 80 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str117, 1*3600}, + {-1}, +#line 98 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str119, 2*3600}, +#line 316 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str120,43200}, +#line 58 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str121, -(2*3600+1800)}, #line 187 "zonetab.list" - {gperf_offsetof(stringpool, 133),16200}, -#line 132 "zonetab.list" - {gperf_offsetof(stringpool, 134), -10800}, -#line 121 "zonetab.list" - {gperf_offsetof(stringpool, 135), -21600}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str122,31500}, +#line 207 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str123,45900}, +#line 213 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str124,-18000}, +#line 201 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str125,14400}, +#line 60 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str126, -4*3600}, +#line 200 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str127,18000}, +#line 57 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str128,-3*3600}, +#line 256 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str129,-30600}, +#line 94 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str130, 4*3600}, +#line 102 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str131, 9*3600}, +#line 125 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str132, 21600}, +#line 190 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str133,16200}, +#line 135 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str134, -10800}, +#line 124 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str135, -21600}, {-1}, -#line 236 "zonetab.list" - {gperf_offsetof(stringpool, 137),25200}, +#line 239 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str137,25200}, {-1}, {-1}, {-1}, {-1}, {-1}, -#line 274 "zonetab.list" - {gperf_offsetof(stringpool, 143),36000}, -#line 266 "zonetab.list" - {gperf_offsetof(stringpool, 144),43200}, -#line 146 "zonetab.list" - {gperf_offsetof(stringpool, 145), -21600}, -#line 193 "zonetab.list" - {gperf_offsetof(stringpool, 146),32400}, -#line 220 "zonetab.list" - {gperf_offsetof(stringpool, 147),-3600}, -#line 214 "zonetab.list" - {gperf_offsetof(stringpool, 148),25200}, -#line 219 "zonetab.list" - {gperf_offsetof(stringpool, 149),0}, -#line 275 "zonetab.list" - {gperf_offsetof(stringpool, 150),46800}, -#line 109 "zonetab.list" - {gperf_offsetof(stringpool, 151), -32400}, +#line 277 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str143,36000}, +#line 269 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str144,43200}, +#line 149 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str145, -21600}, +#line 196 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str146,32400}, +#line 223 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str147,-3600}, +#line 217 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str148,25200}, +#line 222 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str149,0}, +#line 278 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str150,46800}, +#line 112 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str151, -32400}, {-1}, {-1}, -#line 68 "zonetab.list" - {gperf_offsetof(stringpool, 154), -11*3600}, +#line 71 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str154, -11*3600}, {-1}, {-1}, {-1}, -#line 321 "zonetab.list" - {gperf_offsetof(stringpool, 158),0}, +#line 324 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str158,0}, {-1}, -#line 178 "zonetab.list" - {gperf_offsetof(stringpool, 160), 18000}, #line 181 "zonetab.list" - {gperf_offsetof(stringpool, 161),37800}, -#line 265 "zonetab.list" - {gperf_offsetof(stringpool, 162),20700}, -#line 249 "zonetab.list" - {gperf_offsetof(stringpool, 163),37800}, -#line 108 "zonetab.list" - {gperf_offsetof(stringpool, 164), 16200}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str160, 18000}, +#line 184 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str161,37800}, +#line 268 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str162,20700}, +#line 252 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str163,37800}, +#line 111 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str164, 16200}, {-1}, {-1}, +#line 33 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str167, 10*3600}, + {-1}, #line 30 "zonetab.list" - {gperf_offsetof(stringpool, 167), 10*3600}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str169, 7*3600}, +#line 242 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str170,16200}, +#line 209 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str171,28800}, +#line 208 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str172,32400}, +#line 15 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str173, 0*3600}, +#line 232 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str174,14400}, +#line 267 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str175,25200}, +#line 266 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str176,25200}, +#line 226 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str177,43200}, +#line 43 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str178, -8*3600}, +#line 225 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str179,46800}, {-1}, -#line 27 "zonetab.list" - {gperf_offsetof(stringpool, 169), 7*3600}, -#line 239 "zonetab.list" - {gperf_offsetof(stringpool, 170),16200}, -#line 206 "zonetab.list" - {gperf_offsetof(stringpool, 171),28800}, -#line 205 "zonetab.list" - {gperf_offsetof(stringpool, 172),32400}, -#line 12 "zonetab.list" - {gperf_offsetof(stringpool, 173), 0*3600}, -#line 229 "zonetab.list" - {gperf_offsetof(stringpool, 174),14400}, -#line 264 "zonetab.list" - {gperf_offsetof(stringpool, 175),25200}, +#line 285 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str181,-10800}, #line 263 "zonetab.list" - {gperf_offsetof(stringpool, 176),25200}, -#line 223 "zonetab.list" - {gperf_offsetof(stringpool, 177),43200}, -#line 40 "zonetab.list" - {gperf_offsetof(stringpool, 178), -8*3600}, -#line 222 "zonetab.list" - {gperf_offsetof(stringpool, 179),46800}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str182,39600}, +#line 103 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str183, 9*3600}, +#line 247 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str184,39600}, +#line 105 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str185, 10*3600}, +#line 146 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str186, 12600}, +#line 132 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str187, 10800}, +#line 101 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str188, 8*3600}, +#line 42 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str189, -7*3600}, +#line 133 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str190, 36000}, +#line 41 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str191, -6*3600}, +#line 206 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str192,49500}, +#line 301 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str193,18000}, +#line 212 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str194,-14400}, +#line 194 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str195,-43200}, {-1}, -#line 282 "zonetab.list" - {gperf_offsetof(stringpool, 181),-10800}, +#line 262 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str197,28800}, +#line 182 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str198, 36000}, #line 260 "zonetab.list" - {gperf_offsetof(stringpool, 182),39600}, -#line 100 "zonetab.list" - {gperf_offsetof(stringpool, 183), 9*3600}, -#line 244 "zonetab.list" - {gperf_offsetof(stringpool, 184),39600}, -#line 102 "zonetab.list" - {gperf_offsetof(stringpool, 185), 10*3600}, -#line 143 "zonetab.list" - {gperf_offsetof(stringpool, 186), 12600}, -#line 129 "zonetab.list" - {gperf_offsetof(stringpool, 187), 10800}, -#line 98 "zonetab.list" - {gperf_offsetof(stringpool, 188), 8*3600}, -#line 39 "zonetab.list" - {gperf_offsetof(stringpool, 189), -7*3600}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str199,14400}, +#line 322 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str200,32400}, +#line 87 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str201, 2*3600}, +#line 289 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str202,39600}, +#line 155 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str203, 43200}, +#line 303 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str204,46800}, #line 130 "zonetab.list" - {gperf_offsetof(stringpool, 190), 36000}, -#line 38 "zonetab.list" - {gperf_offsetof(stringpool, 191), -6*3600}, -#line 203 "zonetab.list" - {gperf_offsetof(stringpool, 192),49500}, -#line 298 "zonetab.list" - {gperf_offsetof(stringpool, 193),18000}, -#line 209 "zonetab.list" - {gperf_offsetof(stringpool, 194),-14400}, -#line 191 "zonetab.list" - {gperf_offsetof(stringpool, 195),-43200}, - {-1}, -#line 259 "zonetab.list" - {gperf_offsetof(stringpool, 197),28800}, -#line 179 "zonetab.list" - {gperf_offsetof(stringpool, 198), 36000}, -#line 257 "zonetab.list" - {gperf_offsetof(stringpool, 199),14400}, -#line 319 "zonetab.list" - {gperf_offsetof(stringpool, 200),32400}, -#line 84 "zonetab.list" - {gperf_offsetof(stringpool, 201), 2*3600}, -#line 286 "zonetab.list" - {gperf_offsetof(stringpool, 202),39600}, -#line 152 "zonetab.list" - {gperf_offsetof(stringpool, 203), 43200}, -#line 300 "zonetab.list" - {gperf_offsetof(stringpool, 204),46800}, -#line 127 "zonetab.list" - {gperf_offsetof(stringpool, 205), 28800}, -#line 299 "zonetab.list" - {gperf_offsetof(stringpool, 206),50400}, -#line 85 "zonetab.list" - {gperf_offsetof(stringpool, 207), -11*3600}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str205, 28800}, +#line 302 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str206,50400}, +#line 88 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str207, -11*3600}, {-1}, -#line 142 "zonetab.list" - {gperf_offsetof(stringpool, 209), 19800}, +#line 145 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str209, 19800}, {-1}, -#line 314 "zonetab.list" - {gperf_offsetof(stringpool, 211),-10800}, -#line 288 "zonetab.list" - {gperf_offsetof(stringpool, 212),39600}, +#line 317 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str211,-10800}, +#line 291 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str212,39600}, {-1}, -#line 196 "zonetab.list" - {gperf_offsetof(stringpool, 214),-3600}, -#line 195 "zonetab.list" - {gperf_offsetof(stringpool, 215),0}, -#line 293 "zonetab.list" - {gperf_offsetof(stringpool, 216),-36000}, -#line 106 "zonetab.list" - {gperf_offsetof(stringpool, 217), 12*3600}, +#line 199 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str214,-3600}, +#line 198 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str215,0}, +#line 296 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str216,-36000}, +#line 109 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str217, 12*3600}, +#line 131 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str218, -43200}, +#line 108 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str219,12*3600}, +#line 173 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str220, 32400}, #line 128 "zonetab.list" - {gperf_offsetof(stringpool, 218), -43200}, -#line 105 "zonetab.list" - {gperf_offsetof(stringpool, 219),12*3600}, -#line 170 "zonetab.list" - {gperf_offsetof(stringpool, 220), 32400}, -#line 125 "zonetab.list" - {gperf_offsetof(stringpool, 221), 39600}, - {-1}, -#line 283 "zonetab.list" - {gperf_offsetof(stringpool, 223),21600}, -#line 113 "zonetab.list" - {gperf_offsetof(stringpool, 224), -14400}, -#line 262 "zonetab.list" - {gperf_offsetof(stringpool, 225),39600}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str221, 39600}, {-1}, -#line 11 "zonetab.list" - {gperf_offsetof(stringpool, 227), 0*3600}, -#line 301 "zonetab.list" - {gperf_offsetof(stringpool, 228),10800}, -#line 315 "zonetab.list" - {gperf_offsetof(stringpool, 229),43200}, -#line 291 "zonetab.list" - {gperf_offsetof(stringpool, 230),-10800}, -#line 20 "zonetab.list" - {gperf_offsetof(stringpool, 231), -7*3600}, -#line 248 "zonetab.list" - {gperf_offsetof(stringpool, 232),39600}, +#line 286 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str223,21600}, +#line 116 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str224, -14400}, +#line 265 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str225,39600}, {-1}, -#line 52 "zonetab.list" - {gperf_offsetof(stringpool, 234), -3*3600}, #line 14 "zonetab.list" - {gperf_offsetof(stringpool, 235), -4*3600}, - {-1}, {-1}, -#line 277 "zonetab.list" - {gperf_offsetof(stringpool, 238),18000}, -#line 188 "zonetab.list" - {gperf_offsetof(stringpool, 239),21600}, -#line 320 "zonetab.list" - {gperf_offsetof(stringpool, 240),28800}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str227, 0*3600}, +#line 304 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str228,10800}, +#line 318 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str229,43200}, +#line 294 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str230,-10800}, +#line 23 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str231, -7*3600}, +#line 251 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str232,39600}, {-1}, -#line 317 "zonetab.list" - {gperf_offsetof(stringpool, 242),-10800}, -#line 60 "zonetab.list" - {gperf_offsetof(stringpool, 243),-9*3600}, -#line 316 "zonetab.list" - {gperf_offsetof(stringpool, 244),-7200}, +#line 55 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str234, -3*3600}, +#line 17 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str235, -4*3600}, + {-1}, {-1}, +#line 280 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str238,18000}, +#line 191 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str239,21600}, +#line 323 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str240,28800}, {-1}, -#line 246 "zonetab.list" - {gperf_offsetof(stringpool, 246),25200}, -#line 245 "zonetab.list" - {gperf_offsetof(stringpool, 247),28800}, -#line 147 "zonetab.list" - {gperf_offsetof(stringpool, 248), -7200}, -#line 18 "zonetab.list" - {gperf_offsetof(stringpool, 249), -6*3600}, -#line 250 "zonetab.list" - {gperf_offsetof(stringpool, 250),50400}, -#line 165 "zonetab.list" - {gperf_offsetof(stringpool, 251), 28800}, -#line 16 "zonetab.list" - {gperf_offsetof(stringpool, 252), -5*3600}, -#line 76 "zonetab.list" - {gperf_offsetof(stringpool, 253), 1*3600}, +#line 320 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str242,-7200}, +#line 63 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str243,-9*3600}, +#line 319 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str244,-3600}, {-1}, -#line 164 "zonetab.list" - {gperf_offsetof(stringpool, 255), 25200}, -#line 41 "zonetab.list" - {gperf_offsetof(stringpool, 256), -9*3600}, +#line 249 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str246,25200}, +#line 248 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str247,28800}, +#line 150 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str248, -7200}, +#line 21 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str249, -6*3600}, +#line 253 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str250,50400}, +#line 168 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str251, 28800}, +#line 19 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str252, -5*3600}, +#line 79 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str253, 1*3600}, {-1}, -#line 171 "zonetab.list" - {gperf_offsetof(stringpool, 258), 46800}, -#line 211 "zonetab.list" - {gperf_offsetof(stringpool, 259),-36000}, +#line 167 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str255, 25200}, +#line 44 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str256, -9*3600}, {-1}, -#line 308 "zonetab.list" - {gperf_offsetof(stringpool, 261),-14400}, -#line 119 "zonetab.list" - {gperf_offsetof(stringpool, 262), 14400}, -#line 123 "zonetab.list" - {gperf_offsetof(stringpool, 263), 3600}, -#line 28 "zonetab.list" - {gperf_offsetof(stringpool, 264), 8*3600}, -#line 124 "zonetab.list" - {gperf_offsetof(stringpool, 265), 3600}, -#line 153 "zonetab.list" - {gperf_offsetof(stringpool, 266), -12600}, -#line 110 "zonetab.list" - {gperf_offsetof(stringpool, 267), 10800}, -#line 289 "zonetab.list" - {gperf_offsetof(stringpool, 268),14400}, -#line 112 "zonetab.list" - {gperf_offsetof(stringpool, 269), 10800}, -#line 111 "zonetab.list" - {gperf_offsetof(stringpool, 270), 14400}, -#line 216 "zonetab.list" - {gperf_offsetof(stringpool, 271),36000}, +#line 174 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str258, 46800}, +#line 214 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str259,-36000}, {-1}, #line 311 "zonetab.list" - {gperf_offsetof(stringpool, 273),21600}, -#line 66 "zonetab.list" - {gperf_offsetof(stringpool, 274),-10*3600}, -#line 151 "zonetab.list" - {gperf_offsetof(stringpool, 275), 20700}, -#line 267 "zonetab.list" - {gperf_offsetof(stringpool, 276),-39600}, -#line 225 "zonetab.list" - {gperf_offsetof(stringpool, 277),-14400}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str261,-14400}, +#line 122 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str262, 14400}, +#line 126 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str263, 3600}, +#line 31 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str264, 8*3600}, +#line 127 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str265, 3600}, +#line 156 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str266, -12600}, +#line 113 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str267, 10800}, +#line 292 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str268,14400}, +#line 115 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str269, 10800}, +#line 114 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str270, 14400}, +#line 219 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str271,36000}, {-1}, -#line 224 "zonetab.list" - {gperf_offsetof(stringpool, 279),-10800}, -#line 67 "zonetab.list" - {gperf_offsetof(stringpool, 280),-10*3600}, -#line 237 "zonetab.list" - {gperf_offsetof(stringpool, 281),10800}, +#line 314 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str273,21600}, +#line 69 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str274,-10*3600}, +#line 154 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str275, 20700}, +#line 270 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str276,-39600}, +#line 228 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str277,-14400}, + {-1}, +#line 227 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str279,-10800}, +#line 70 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str280,-10*3600}, +#line 240 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str281,10800}, {-1}, {-1}, -#line 297 "zonetab.list" - {gperf_offsetof(stringpool, 284),32400}, -#line 175 "zonetab.list" - {gperf_offsetof(stringpool, 285), 28800}, -#line 134 "zonetab.list" - {gperf_offsetof(stringpool, 286), 7200}, -#line 149 "zonetab.list" - {gperf_offsetof(stringpool, 287), 23400}, -#line 107 "zonetab.list" - {gperf_offsetof(stringpool, 288),13*3600}, -#line 230 "zonetab.list" - {gperf_offsetof(stringpool, 289),-10800}, -#line 307 "zonetab.list" - {gperf_offsetof(stringpool, 290),18000}, +#line 300 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str284,32400}, +#line 178 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str285, 28800}, +#line 137 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str286, 7200}, +#line 152 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str287, 23400}, +#line 110 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str288,13*3600}, +#line 233 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str289,-10800}, +#line 310 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str290,18000}, {-1}, {-1}, -#line 155 "zonetab.list" - {gperf_offsetof(stringpool, 293), 25200}, -#line 258 "zonetab.list" - {gperf_offsetof(stringpool, 294),18000}, -#line 227 "zonetab.list" - {gperf_offsetof(stringpool, 295),-21600}, +#line 158 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str293, 25200}, #line 261 "zonetab.list" - {gperf_offsetof(stringpool, 296),43200}, -#line 213 "zonetab.list" - {gperf_offsetof(stringpool, 297),-3600}, -#line 154 "zonetab.list" - {gperf_offsetof(stringpool, 298), 28800}, - {-1}, -#line 243 "zonetab.list" - {gperf_offsetof(stringpool, 300),21600}, -#line 114 "zonetab.list" - {gperf_offsetof(stringpool, 301), 34200}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str294,18000}, +#line 230 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str295,-21600}, +#line 264 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str296,43200}, +#line 216 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str297,-3600}, #line 157 "zonetab.list" - {gperf_offsetof(stringpool, 302), -28800}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str298, 28800}, {-1}, +#line 246 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str300,21600}, #line 117 "zonetab.list" - {gperf_offsetof(stringpool, 304), -21600}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str301, 34200}, +#line 160 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str302, -28800}, {-1}, -#line 156 "zonetab.list" - {gperf_offsetof(stringpool, 306), -14400}, -#line 116 "zonetab.list" - {gperf_offsetof(stringpool, 307), -3600}, -#line 228 "zonetab.list" - {gperf_offsetof(stringpool, 308),-32400}, -#line 294 "zonetab.list" - {gperf_offsetof(stringpool, 309),18000}, -#line 37 "zonetab.list" - {gperf_offsetof(stringpool, 310), -5*3600}, -#line 137 "zonetab.list" - {gperf_offsetof(stringpool, 311), 7200}, -#line 58 "zonetab.list" - {gperf_offsetof(stringpool, 312),-8*3600}, -#line 304 "zonetab.list" - {gperf_offsetof(stringpool, 313),28800}, -#line 303 "zonetab.list" - {gperf_offsetof(stringpool, 314),32400}, -#line 284 "zonetab.list" - {gperf_offsetof(stringpool, 315),14400}, +#line 120 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str304, -21600}, {-1}, -#line 295 "zonetab.list" - {gperf_offsetof(stringpool, 317),18000}, +#line 159 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str306, -14400}, +#line 119 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str307, -3600}, +#line 231 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str308,-32400}, +#line 297 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str309,18000}, +#line 40 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str310, -5*3600}, +#line 140 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str311, 7200}, +#line 61 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str312,-8*3600}, +#line 307 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str313,28800}, +#line 306 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str314,32400}, +#line 287 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str315,14400}, {-1}, -#line 166 "zonetab.list" - {gperf_offsetof(stringpool, 319), 7200}, +#line 298 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str317,18000}, + {-1}, +#line 169 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str319, 7200}, {-1}, {-1}, {-1}, {-1}, -#line 97 "zonetab.list" - {gperf_offsetof(stringpool, 324), 8*3600}, +#line 100 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str324, 8*3600}, {-1}, -#line 50 "zonetab.list" - {gperf_offsetof(stringpool, 326), -(1*3600+1800)}, -#line 285 "zonetab.list" - {gperf_offsetof(stringpool, 327),-10800}, +#line 53 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str326, -(1*3600+1800)}, +#line 288 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str327,-10800}, {-1}, {-1}, -#line 287 "zonetab.list" - {gperf_offsetof(stringpool, 330),14400}, +#line 290 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str330,14400}, {-1}, -#line 169 "zonetab.list" - {gperf_offsetof(stringpool, 332), 36000}, +#line 172 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str332, 36000}, {-1}, -#line 235 "zonetab.list" - {gperf_offsetof(stringpool, 334),25200}, -#line 234 "zonetab.list" - {gperf_offsetof(stringpool, 335),28800}, +#line 238 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str334,25200}, +#line 237 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str335,28800}, {-1}, {-1}, -#line 232 "zonetab.list" - {gperf_offsetof(stringpool, 338),-14400}, +#line 235 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str338,-14400}, {-1}, {-1}, {-1}, -#line 44 "zonetab.list" - {gperf_offsetof(stringpool, 342), -12*3600}, -#line 61 "zonetab.list" - {gperf_offsetof(stringpool, 343),-9*3600}, -#line 162 "zonetab.list" - {gperf_offsetof(stringpool, 344), -14400}, -#line 141 "zonetab.list" - {gperf_offsetof(stringpool, 345), -36000}, +#line 47 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str342, -12*3600}, +#line 64 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str343,-9*3600}, +#line 165 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str344, -14400}, +#line 144 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str345, -36000}, {-1}, -#line 306 "zonetab.list" - {gperf_offsetof(stringpool, 347),-10800}, +#line 309 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str347,-10800}, {-1}, -#line 305 "zonetab.list" - {gperf_offsetof(stringpool, 349),-7200}, +#line 308 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str349,-7200}, +#line 329 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str350,18000}, +#line 328 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str351,21600}, +#line 250 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str352,14400}, #line 326 "zonetab.list" - {gperf_offsetof(stringpool, 350),18000}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str353,32400}, #line 325 "zonetab.list" - {gperf_offsetof(stringpool, 351),21600}, -#line 247 "zonetab.list" - {gperf_offsetof(stringpool, 352),14400}, -#line 323 "zonetab.list" - {gperf_offsetof(stringpool, 353),32400}, -#line 322 "zonetab.list" - {gperf_offsetof(stringpool, 354),36000}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str354,36000}, {-1}, {-1}, {-1}, -#line 63 "zonetab.list" - {gperf_offsetof(stringpool, 358), -9*3600}, -#line 144 "zonetab.list" - {gperf_offsetof(stringpool, 359), 7200}, +#line 66 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str358, -9*3600}, +#line 147 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str359, 7200}, {-1}, {-1}, {-1}, {-1}, {-1}, -#line 167 "zonetab.list" - {gperf_offsetof(stringpool, 365), 21600}, +#line 170 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str365, 21600}, {-1}, -#line 180 "zonetab.list" - {gperf_offsetof(stringpool, 367), 32400}, +#line 183 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str367, 32400}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, -#line 318 "zonetab.list" - {gperf_offsetof(stringpool, 375),25200}, +#line 321 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str375,25200}, {-1}, -#line 115 "zonetab.list" - {gperf_offsetof(stringpool, 377), 36000}, -#line 231 "zonetab.list" - {gperf_offsetof(stringpool, 378),43200}, +#line 118 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str377, 36000}, +#line 234 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str378,43200}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, -#line 173 "zonetab.list" - {gperf_offsetof(stringpool, 387), -25200}, +#line 176 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str387, -25200}, {-1}, {-1}, {-1}, -#line 310 "zonetab.list" - {gperf_offsetof(stringpool, 391),36000}, -#line 309 "zonetab.list" - {gperf_offsetof(stringpool, 392),39600}, +#line 313 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str391,36000}, +#line 312 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str392,39600}, {-1}, {-1}, -#line 140 "zonetab.list" - {gperf_offsetof(stringpool, 395), 7200}, +#line 143 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str395, 7200}, {-1}, {-1}, -#line 168 "zonetab.list" - {gperf_offsetof(stringpool, 398), 28800}, -#line 290 "zonetab.list" - {gperf_offsetof(stringpool, 399),39600}, +#line 171 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str398, 28800}, +#line 293 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str399,39600}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, -#line 118 "zonetab.list" - {gperf_offsetof(stringpool, 408), -3600}, +#line 121 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str408, -3600}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, -#line 296 "zonetab.list" - {gperf_offsetof(stringpool, 417),46800}, -#line 163 "zonetab.list" - {gperf_offsetof(stringpool, 418), -39600}, +#line 299 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str417,46800}, +#line 166 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str418, -39600}, {-1}, {-1}, -#line 161 "zonetab.list" - {gperf_offsetof(stringpool, 421), -18000}, +#line 164 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str421, -18000}, {-1}, {-1}, {-1}, {-1}, {-1}, -#line 312 "zonetab.list" - {gperf_offsetof(stringpool, 427),39600}, -#line 69 "zonetab.list" - {gperf_offsetof(stringpool, 428),-12*3600}, +#line 315 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str427,39600}, +#line 72 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str428,-12*3600}, {-1}, {-1}, {-1}, -#line 136 "zonetab.list" - {gperf_offsetof(stringpool, 432), 43200}, +#line 139 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str432, 43200}, {-1}, {-1}, -#line 46 "zonetab.list" - {gperf_offsetof(stringpool, 435), 0*3600}, +#line 49 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str435, 0*3600}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, -#line 145 "zonetab.list" - {gperf_offsetof(stringpool, 443), 32400}, +#line 148 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str443, 32400}, {-1}, -#line 131 "zonetab.list" - {gperf_offsetof(stringpool, 445), 7200}, +#line 134 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str445, 7200}, {-1}, {-1}, {-1}, -#line 292 "zonetab.list" - {gperf_offsetof(stringpool, 449),10800}, +#line 295 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str449,10800}, {-1}, {-1}, -#line 150 "zonetab.list" - {gperf_offsetof(stringpool, 452), 21600}, +#line 153 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str452, 21600}, {-1}, {-1}, -#line 302 "zonetab.list" - {gperf_offsetof(stringpool, 455),43200}, +#line 305 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str455,43200}, {-1}, {-1}, -#line 176 "zonetab.list" - {gperf_offsetof(stringpool, 458), 3600}, +#line 179 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str458, 3600}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, -#line 135 "zonetab.list" - {gperf_offsetof(stringpool, 466), 18000}, +#line 138 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str466, 18000}, {-1}, -#line 174 "zonetab.list" - {gperf_offsetof(stringpool, 468), 36000}, +#line 177 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str468, 36000}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, -#line 324 "zonetab.list" - {gperf_offsetof(stringpool, 476),36000}, -#line 172 "zonetab.list" - {gperf_offsetof(stringpool, 477), -18000}, +#line 327 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str476,36000}, +#line 175 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str477, -18000}, {-1}, {-1}, {-1}, {-1}, -#line 160 "zonetab.list" - {gperf_offsetof(stringpool, 482), -10800}, +#line 163 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str482, -10800}, {-1}, {-1}, -#line 62 "zonetab.list" - {gperf_offsetof(stringpool, 485), -9*3600}, -#line 159 "zonetab.list" - {gperf_offsetof(stringpool, 486), 10800}, +#line 65 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str485, -9*3600}, +#line 162 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str486, 10800}, {-1}, {-1}, {-1}, {-1}, {-1}, -#line 233 "zonetab.list" - {gperf_offsetof(stringpool, 492),28800}, +#line 236 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str492,28800}, {-1}, {-1}, {-1}, {-1}, -#line 158 "zonetab.list" - {gperf_offsetof(stringpool, 497), 3600}, +#line 161 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str497, 3600}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, -#line 177 "zonetab.list" - {gperf_offsetof(stringpool, 540), 3600}, +#line 180 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str540, 3600}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, -#line 59 "zonetab.list" - {gperf_offsetof(stringpool, 563), -8*3600}, +#line 62 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str563, -8*3600}, {-1}, {-1}, -#line 104 "zonetab.list" - {gperf_offsetof(stringpool, 566),12*3600}, -#line 139 "zonetab.list" - {gperf_offsetof(stringpool, 567), 0}, +#line 107 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str566,12*3600}, +#line 142 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str567, 0}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, -#line 138 "zonetab.list" - {gperf_offsetof(stringpool, 619), -10800} +#line 141 "zonetab.list" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str619, -10800} }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) @@ -1558,5 +1560,5 @@ zonetab (register const char *str, register size_t len) } return 0; } -#line 327 "zonetab.list" +#line 330 "zonetab.list" diff --git a/ext/date/zonetab.list b/ext/date/zonetab.list index 748aec1d8a..63b6873447 100644 --- a/ext/date/zonetab.list +++ b/ext/date/zonetab.list @@ -1,4 +1,7 @@ %{ +#define GPERF_DOWNCASE 1 +#define GPERF_CASE_STRNCMP 1 +#define gperf_case_strncmp strncasecmp struct zone { int name; int offset; @@ -313,8 +316,8 @@ vut,39600 wakt,43200 warst,-10800 wft,43200 -wgst,-7200 -wgt,-10800 +wgst,-3600 +wgt,-7200 wib,25200 wit,32400 wita,28800 diff --git a/ext/digest/.document b/ext/digest/.document new file mode 100644 index 0000000000..beab275b5a --- /dev/null +++ b/ext/digest/.document @@ -0,0 +1,3 @@ +digest.c +bubblebabble/bubblebabble.c +*/*init.c diff --git a/ext/digest/bubblebabble/bubblebabble.c b/ext/digest/bubblebabble/bubblebabble.c index 358ab416b9..dac603c0d7 100644 --- a/ext/digest/bubblebabble/bubblebabble.c +++ b/ext/digest/bubblebabble/bubblebabble.c @@ -129,15 +129,14 @@ Init_bubblebabble(void) rb_require("digest"); - rb_mDigest = rb_path2class("Digest"); - rb_mDigest_Instance = rb_path2class("Digest::Instance"); - rb_cDigest_Class = rb_path2class("Digest::Class"); - #if 0 rb_mDigest = rb_define_module("Digest"); rb_mDigest_Instance = rb_define_module_under(rb_mDigest, "Instance"); rb_cDigest_Class = rb_define_class_under(rb_mDigest, "Class", rb_cObject); #endif + rb_mDigest = rb_digest_namespace(); + rb_mDigest_Instance = rb_const_get(rb_mDigest, rb_intern_const("Instance")); + rb_cDigest_Class = rb_const_get(rb_mDigest, rb_intern_const("Class")); rb_define_module_function(rb_mDigest, "bubblebabble", rb_digest_s_bubblebabble, 1); rb_define_singleton_method(rb_cDigest_Class, "bubblebabble", rb_digest_class_s_bubblebabble, -1); diff --git a/ext/digest/bubblebabble/depend b/ext/digest/bubblebabble/depend index 6f0003a66d..02d88e960c 100644 --- a/ext/digest/bubblebabble/depend +++ b/ext/digest/bubblebabble/depend @@ -128,6 +128,7 @@ bubblebabble.o: $(hdrdir)/ruby/internal/intern/re.h bubblebabble.o: $(hdrdir)/ruby/internal/intern/ruby.h bubblebabble.o: $(hdrdir)/ruby/internal/intern/select.h bubblebabble.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +bubblebabble.o: $(hdrdir)/ruby/internal/intern/set.h bubblebabble.o: $(hdrdir)/ruby/internal/intern/signal.h bubblebabble.o: $(hdrdir)/ruby/internal/intern/sprintf.h bubblebabble.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ bubblebabble.o: $(hdrdir)/ruby/internal/special_consts.h bubblebabble.o: $(hdrdir)/ruby/internal/static_assert.h bubblebabble.o: $(hdrdir)/ruby/internal/stdalign.h bubblebabble.o: $(hdrdir)/ruby/internal/stdbool.h +bubblebabble.o: $(hdrdir)/ruby/internal/stdckdint.h bubblebabble.o: $(hdrdir)/ruby/internal/symbol.h bubblebabble.o: $(hdrdir)/ruby/internal/value.h bubblebabble.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/digest/defs.h b/ext/digest/defs.h index 77a134f364..9b11f4eca9 100644 --- a/ext/digest/defs.h +++ b/ext/digest/defs.h @@ -16,4 +16,26 @@ # define __END_DECLS #endif +#define RB_DIGEST_DIAGNOSTIC(compiler, op, flag) _Pragma(STRINGIZE(compiler diagnostic op flag)) +#ifdef RBIMPL_WARNING_IGNORED +# define RB_DIGEST_WARNING_IGNORED(flag) RBIMPL_WARNING_IGNORED(flag) +# define RB_DIGEST_WARNING_PUSH() RBIMPL_WARNING_PUSH() +# define RB_DIGEST_WARNING_POP() RBIMPL_WARNING_POP() +#elif defined(__clang__) +# define RB_DIGEST_WARNING_IGNORED(flag) RB_DIGEST_DIAGNOSTIC(clang, ignored, #flag) +# define RB_DIGEST_WARNING_PUSH() _Pragma("clang diagnostic push") +# define RB_DIGEST_WARNING_POP() _Pragma("clang diagnostic pop") +#else /* __GNUC__ */ +# define RB_DIGEST_WARNING_IGNORED(flag) RB_DIGEST_DIAGNOSTIC(GCC, ignored, #flag) +# define RB_DIGEST_WARNING_PUSH() _Pragma("GCC diagnostic push") +# define RB_DIGEST_WARNING_POP() _Pragma("GCC diagnostic pop") +#endif +#ifdef RBIMPL_HAS_WARNING +# define RB_DIGEST_HAS_WARNING(_) RBIMPL_HAS_WARNING(_) +#elif defined(__has_warning) +# define RB_DIGEST_HAS_WARNING(_) __has_warning(_) +#else +# define RB_DIGEST_HAS_WARNING(_) 0 +#endif + #endif /* DEFS_H */ diff --git a/ext/digest/depend b/ext/digest/depend index 6df940a679..3eabb1d1d6 100644 --- a/ext/digest/depend +++ b/ext/digest/depend @@ -128,6 +128,7 @@ digest.o: $(hdrdir)/ruby/internal/intern/re.h digest.o: $(hdrdir)/ruby/internal/intern/ruby.h digest.o: $(hdrdir)/ruby/internal/intern/select.h digest.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +digest.o: $(hdrdir)/ruby/internal/intern/set.h digest.o: $(hdrdir)/ruby/internal/intern/signal.h digest.o: $(hdrdir)/ruby/internal/intern/sprintf.h digest.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ digest.o: $(hdrdir)/ruby/internal/special_consts.h digest.o: $(hdrdir)/ruby/internal/static_assert.h digest.o: $(hdrdir)/ruby/internal/stdalign.h digest.o: $(hdrdir)/ruby/internal/stdbool.h +digest.o: $(hdrdir)/ruby/internal/stdckdint.h digest.o: $(hdrdir)/ruby/internal/symbol.h digest.o: $(hdrdir)/ruby/internal/value.h digest.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/digest/digest.c b/ext/digest/digest.c index 68837a674c..bd8d3e815f 100644 --- a/ext/digest/digest.c +++ b/ext/digest/digest.c @@ -534,9 +534,39 @@ rb_digest_class_init(VALUE self) * * * rb_ivar_set(cDigest_SHA1, rb_intern("metadata"), - * Data_Wrap_Struct(0, 0, 0, (void *)&sha1)); + * rb_digest_make_metadata(&sha1)); */ +#ifdef DIGEST_USE_RB_EXT_RESOLVE_SYMBOL +static const rb_data_type_t metadata_type = { + "digest/metadata", + {0}, +}; + +RUBY_FUNC_EXPORTED VALUE +rb_digest_wrap_metadata(const rb_digest_metadata_t *meta) +{ + return rb_obj_freeze(TypedData_Wrap_Struct(0, &metadata_type, (void *)meta)); +} +#endif + +static rb_digest_metadata_t * +get_metadata_ptr(VALUE obj) +{ + rb_digest_metadata_t *algo; + +#ifdef DIGEST_USE_RB_EXT_RESOLVE_SYMBOL + if (!rb_typeddata_is_kind_of(obj, &metadata_type)) return 0; + algo = RTYPEDDATA_DATA(obj); +#else +# undef RUBY_UNTYPED_DATA_WARNING +# define RUBY_UNTYPED_DATA_WARNING 0 + Data_Get_Struct(obj, rb_digest_metadata_t, algo); +#endif + + return algo; +} + static rb_digest_metadata_t * get_digest_base_metadata(VALUE klass) { @@ -554,8 +584,8 @@ get_digest_base_metadata(VALUE klass) if (NIL_P(p)) rb_raise(rb_eRuntimeError, "Digest::Base cannot be directly inherited in Ruby"); - if (!RB_TYPE_P(obj, T_DATA) || RTYPEDDATA_P(obj)) { - wrong: + algo = get_metadata_ptr(obj); + if (!algo) { if (p == klass) rb_raise(rb_eTypeError, "%"PRIsVALUE"::metadata is not initialized properly", klass); @@ -564,12 +594,6 @@ get_digest_base_metadata(VALUE klass) klass, p); } -#undef RUBY_UNTYPED_DATA_WARNING -#define RUBY_UNTYPED_DATA_WARNING 0 - Data_Get_Struct(obj, rb_digest_metadata_t, algo); - - if (!algo) goto wrong; - switch (algo->api_version) { case 3: break; diff --git a/ext/digest/digest.h b/ext/digest/digest.h index 68a3da5dd2..c5c37583a6 100644 --- a/ext/digest/digest.h +++ b/ext/digest/digest.h @@ -64,10 +64,43 @@ rb_id_metadata(void) return rb_intern_const("metadata"); } +#if !defined(HAVE_RB_EXT_RESOLVE_SYMBOL) +#elif !defined(RUBY_UNTYPED_DATA_WARNING) +# error RUBY_UNTYPED_DATA_WARNING is not defined +#elif RUBY_UNTYPED_DATA_WARNING +/* rb_ext_resolve_symbol() has been defined since Ruby 3.3, but digest + * bundled with 3.3 didn't use it. */ +# define DIGEST_USE_RB_EXT_RESOLVE_SYMBOL 1 +#endif + static inline VALUE rb_digest_make_metadata(const rb_digest_metadata_t *meta) { -#undef RUBY_UNTYPED_DATA_WARNING -#define RUBY_UNTYPED_DATA_WARNING 0 +#if defined(EXTSTATIC) && EXTSTATIC + /* The extension is built as a static library, so safe to refer to + * rb_digest_wrap_metadata directly. */ + extern VALUE rb_digest_wrap_metadata(const rb_digest_metadata_t *meta); + return rb_digest_wrap_metadata(meta); +#else + /* The extension is built as a shared library, so we can't refer + * to rb_digest_wrap_metadata directly. */ +# ifdef DIGEST_USE_RB_EXT_RESOLVE_SYMBOL + /* If rb_ext_resolve_symbol() is available, use it to get the address of + * rb_digest_wrap_metadata. */ + typedef VALUE (*wrapper_func_type)(const rb_digest_metadata_t *meta); + static wrapper_func_type wrapper; + if (!wrapper) { + wrapper = (wrapper_func_type)(uintptr_t) + rb_ext_resolve_symbol("digest.so", "rb_digest_wrap_metadata"); + if (!wrapper) rb_raise(rb_eLoadError, "rb_digest_wrap_metadata not found"); + } + return wrapper(meta); +# else + /* If rb_ext_resolve_symbol() is not available, keep using untyped + * data. */ +# undef RUBY_UNTYPED_DATA_WARNING +# define RUBY_UNTYPED_DATA_WARNING 0 return rb_obj_freeze(Data_Wrap_Struct(0, 0, 0, (void *)meta)); +# endif +#endif } diff --git a/ext/digest/digest_conf.rb b/ext/digest/digest_conf.rb index 36a7d75289..099d20fcbe 100644 --- a/ext/digest/digest_conf.rb +++ b/ext/digest/digest_conf.rb @@ -2,14 +2,16 @@ def digest_conf(name) unless with_config("bundled-#{name}") - cc = with_config("common-digest") - if cc != false or /\b#{name}\b/ =~ cc - if File.exist?("#$srcdir/#{name}cc.h") and - have_header("CommonCrypto/CommonDigest.h") - $defs << "-D#{name.upcase}_USE_COMMONDIGEST" - $headers << "#{name}cc.h" - return :commondigest - end + case cc = with_config("common-digest", true) + when true, false + else + cc = cc.split(/[\s,]++/).any? {|pat| File.fnmatch?(pat, name)} + end + if cc and File.exist?("#$srcdir/#{name}cc.h") and + have_header("CommonCrypto/CommonDigest.h") + $defs << "-D#{name.upcase}_USE_COMMONDIGEST" + $headers << "#{name}cc.h" + return :commondigest end end $objs << "#{name}.#{$OBJEXT}" diff --git a/ext/digest/lib/digest/version.rb b/ext/digest/lib/digest/version.rb index 42fd7acf6e..a56e80c54e 100644 --- a/ext/digest/lib/digest/version.rb +++ b/ext/digest/lib/digest/version.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true module Digest - VERSION = "3.1.1" + # The version string + VERSION = "3.2.1" end diff --git a/ext/digest/md5/depend b/ext/digest/md5/depend index da1b345999..d1c25c28c8 100644 --- a/ext/digest/md5/depend +++ b/ext/digest/md5/depend @@ -131,6 +131,7 @@ md5.o: $(hdrdir)/ruby/internal/intern/re.h md5.o: $(hdrdir)/ruby/internal/intern/ruby.h md5.o: $(hdrdir)/ruby/internal/intern/select.h md5.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +md5.o: $(hdrdir)/ruby/internal/intern/set.h md5.o: $(hdrdir)/ruby/internal/intern/signal.h md5.o: $(hdrdir)/ruby/internal/intern/sprintf.h md5.o: $(hdrdir)/ruby/internal/intern/string.h @@ -150,6 +151,7 @@ md5.o: $(hdrdir)/ruby/internal/special_consts.h md5.o: $(hdrdir)/ruby/internal/static_assert.h md5.o: $(hdrdir)/ruby/internal/stdalign.h md5.o: $(hdrdir)/ruby/internal/stdbool.h +md5.o: $(hdrdir)/ruby/internal/stdckdint.h md5.o: $(hdrdir)/ruby/internal/symbol.h md5.o: $(hdrdir)/ruby/internal/value.h md5.o: $(hdrdir)/ruby/internal/value_type.h @@ -292,6 +294,7 @@ md5init.o: $(hdrdir)/ruby/internal/intern/re.h md5init.o: $(hdrdir)/ruby/internal/intern/ruby.h md5init.o: $(hdrdir)/ruby/internal/intern/select.h md5init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +md5init.o: $(hdrdir)/ruby/internal/intern/set.h md5init.o: $(hdrdir)/ruby/internal/intern/signal.h md5init.o: $(hdrdir)/ruby/internal/intern/sprintf.h md5init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -311,6 +314,7 @@ md5init.o: $(hdrdir)/ruby/internal/special_consts.h md5init.o: $(hdrdir)/ruby/internal/static_assert.h md5init.o: $(hdrdir)/ruby/internal/stdalign.h md5init.o: $(hdrdir)/ruby/internal/stdbool.h +md5init.o: $(hdrdir)/ruby/internal/stdckdint.h md5init.o: $(hdrdir)/ruby/internal/symbol.h md5init.o: $(hdrdir)/ruby/internal/value.h md5init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/digest/md5/md5cc.h b/ext/digest/md5/md5cc.h index e34d7d5c11..a002c17604 100644 --- a/ext/digest/md5/md5cc.h +++ b/ext/digest/md5/md5cc.h @@ -1,8 +1,8 @@ #define COMMON_DIGEST_FOR_OPENSSL 1 #include <CommonCrypto/CommonDigest.h> -#ifdef __clang__ -# pragma clang diagnostic ignored "-Wdeprecated-declarations" +#ifdef __GNUC__ +RB_DIGEST_WARNING_IGNORED(-Wdeprecated-declarations) /* Suppress deprecation warnings of MD5 from Xcode 11.1 */ /* Although we know MD5 is deprecated too, provide just for backward * compatibility, as well as Apple does. */ @@ -17,3 +17,11 @@ static DEFINE_FINISH_FUNC_FROM_FINAL(MD5) #undef MD5_Finish #define MD5_Update rb_digest_MD5_update #define MD5_Finish rb_digest_MD5_finish + +/* + * Pre-10.6 defines are with args, which don't match the argless use in + * the function pointer inits. Thus, we redefine MD5_Init as well. + * This is a NOP on 10.6+. + */ +#undef MD5_Init +#define MD5_Init CC_MD5_Init diff --git a/ext/digest/md5/md5init.c b/ext/digest/md5/md5init.c index 52cba78bf1..c919060587 100644 --- a/ext/digest/md5/md5init.c +++ b/ext/digest/md5/md5init.c @@ -3,6 +3,7 @@ #include <ruby/ruby.h> #include "../digest.h" +#include "../defs.h" #if defined(MD5_USE_COMMONDIGEST) #include "md5cc.h" #else @@ -53,9 +54,8 @@ Init_md5(void) mDigest = rb_define_module("Digest"); /* let rdoc know */ #endif mDigest = rb_digest_namespace(); - cDigest_Base = rb_path2class("Digest::Base"); + cDigest_Base = rb_const_get(mDigest, rb_intern_const("Base")); cDigest_MD5 = rb_define_class_under(mDigest, "MD5", cDigest_Base); - rb_iv_set(cDigest_MD5, "metadata", rb_digest_make_metadata(&md5)); } diff --git a/ext/digest/rmd160/depend b/ext/digest/rmd160/depend index abfa08b023..aec484f7b3 100644 --- a/ext/digest/rmd160/depend +++ b/ext/digest/rmd160/depend @@ -131,6 +131,7 @@ rmd160.o: $(hdrdir)/ruby/internal/intern/re.h rmd160.o: $(hdrdir)/ruby/internal/intern/ruby.h rmd160.o: $(hdrdir)/ruby/internal/intern/select.h rmd160.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +rmd160.o: $(hdrdir)/ruby/internal/intern/set.h rmd160.o: $(hdrdir)/ruby/internal/intern/signal.h rmd160.o: $(hdrdir)/ruby/internal/intern/sprintf.h rmd160.o: $(hdrdir)/ruby/internal/intern/string.h @@ -150,6 +151,7 @@ rmd160.o: $(hdrdir)/ruby/internal/special_consts.h rmd160.o: $(hdrdir)/ruby/internal/static_assert.h rmd160.o: $(hdrdir)/ruby/internal/stdalign.h rmd160.o: $(hdrdir)/ruby/internal/stdbool.h +rmd160.o: $(hdrdir)/ruby/internal/stdckdint.h rmd160.o: $(hdrdir)/ruby/internal/symbol.h rmd160.o: $(hdrdir)/ruby/internal/value.h rmd160.o: $(hdrdir)/ruby/internal/value_type.h @@ -292,6 +294,7 @@ rmd160init.o: $(hdrdir)/ruby/internal/intern/re.h rmd160init.o: $(hdrdir)/ruby/internal/intern/ruby.h rmd160init.o: $(hdrdir)/ruby/internal/intern/select.h rmd160init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +rmd160init.o: $(hdrdir)/ruby/internal/intern/set.h rmd160init.o: $(hdrdir)/ruby/internal/intern/signal.h rmd160init.o: $(hdrdir)/ruby/internal/intern/sprintf.h rmd160init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -311,6 +314,7 @@ rmd160init.o: $(hdrdir)/ruby/internal/special_consts.h rmd160init.o: $(hdrdir)/ruby/internal/static_assert.h rmd160init.o: $(hdrdir)/ruby/internal/stdalign.h rmd160init.o: $(hdrdir)/ruby/internal/stdbool.h +rmd160init.o: $(hdrdir)/ruby/internal/stdckdint.h rmd160init.o: $(hdrdir)/ruby/internal/symbol.h rmd160init.o: $(hdrdir)/ruby/internal/value.h rmd160init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/digest/rmd160/rmd160init.c b/ext/digest/rmd160/rmd160init.c index 2ae81ec4d6..e4b707ed9e 100644 --- a/ext/digest/rmd160/rmd160init.c +++ b/ext/digest/rmd160/rmd160init.c @@ -49,9 +49,8 @@ Init_rmd160(void) mDigest = rb_define_module("Digest"); /* let rdoc know */ #endif mDigest = rb_digest_namespace(); - cDigest_Base = rb_path2class("Digest::Base"); + cDigest_Base = rb_const_get(mDigest, rb_intern_const("Base")); cDigest_RMD160 = rb_define_class_under(mDigest, "RMD160", cDigest_Base); - rb_iv_set(cDigest_RMD160, "metadata", rb_digest_make_metadata(&rmd160)); } diff --git a/ext/digest/sha1/depend b/ext/digest/sha1/depend index d17338e92b..e6bd9d8f73 100644 --- a/ext/digest/sha1/depend +++ b/ext/digest/sha1/depend @@ -131,6 +131,7 @@ sha1.o: $(hdrdir)/ruby/internal/intern/re.h sha1.o: $(hdrdir)/ruby/internal/intern/ruby.h sha1.o: $(hdrdir)/ruby/internal/intern/select.h sha1.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +sha1.o: $(hdrdir)/ruby/internal/intern/set.h sha1.o: $(hdrdir)/ruby/internal/intern/signal.h sha1.o: $(hdrdir)/ruby/internal/intern/sprintf.h sha1.o: $(hdrdir)/ruby/internal/intern/string.h @@ -150,6 +151,7 @@ sha1.o: $(hdrdir)/ruby/internal/special_consts.h sha1.o: $(hdrdir)/ruby/internal/static_assert.h sha1.o: $(hdrdir)/ruby/internal/stdalign.h sha1.o: $(hdrdir)/ruby/internal/stdbool.h +sha1.o: $(hdrdir)/ruby/internal/stdckdint.h sha1.o: $(hdrdir)/ruby/internal/symbol.h sha1.o: $(hdrdir)/ruby/internal/value.h sha1.o: $(hdrdir)/ruby/internal/value_type.h @@ -292,6 +294,7 @@ sha1init.o: $(hdrdir)/ruby/internal/intern/re.h sha1init.o: $(hdrdir)/ruby/internal/intern/ruby.h sha1init.o: $(hdrdir)/ruby/internal/intern/select.h sha1init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +sha1init.o: $(hdrdir)/ruby/internal/intern/set.h sha1init.o: $(hdrdir)/ruby/internal/intern/signal.h sha1init.o: $(hdrdir)/ruby/internal/intern/sprintf.h sha1init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -311,6 +314,7 @@ sha1init.o: $(hdrdir)/ruby/internal/special_consts.h sha1init.o: $(hdrdir)/ruby/internal/static_assert.h sha1init.o: $(hdrdir)/ruby/internal/stdalign.h sha1init.o: $(hdrdir)/ruby/internal/stdbool.h +sha1init.o: $(hdrdir)/ruby/internal/stdckdint.h sha1init.o: $(hdrdir)/ruby/internal/symbol.h sha1init.o: $(hdrdir)/ruby/internal/value.h sha1init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/digest/sha1/sha1.c b/ext/digest/sha1/sha1.c index 5311227549..ce200270b7 100644 --- a/ext/digest/sha1/sha1.c +++ b/ext/digest/sha1/sha1.c @@ -232,8 +232,14 @@ void SHA1_Update(SHA1_CTX *context, const uint8_t *data, size_t len) if ((j + len) > 63) { (void)memcpy(&context->buffer[j], data, (i = 64-j)); SHA1_Transform(context->state, context->buffer); - for ( ; i + 63 < len; i += 64) + for ( ; i + 63 < len; i += 64) { + RB_DIGEST_WARNING_PUSH(); +#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 11 + RB_DIGEST_WARNING_IGNORED(-Wstringop-overread); +#endif SHA1_Transform(context->state, &data[i]); + RB_DIGEST_WARNING_POP(); + } j = 0; } else { i = 0; diff --git a/ext/digest/sha1/sha1cc.h b/ext/digest/sha1/sha1cc.h index 2ed8d646ab..f39ecc6234 100644 --- a/ext/digest/sha1/sha1cc.h +++ b/ext/digest/sha1/sha1cc.h @@ -12,3 +12,11 @@ static DEFINE_FINISH_FUNC_FROM_FINAL(SHA1) #undef SHA1_Finish #define SHA1_Update rb_digest_SHA1_update #define SHA1_Finish rb_digest_SHA1_finish + +/* + * Pre-10.6 defines are with args, which don't match the argless use in + * the function pointer inits. Thus, we redefine SHA1_Init as well. + * This is a NOP on 10.6+. + */ +#undef SHA1_Init +#define SHA1_Init CC_SHA1_Init diff --git a/ext/digest/sha1/sha1init.c b/ext/digest/sha1/sha1init.c index f7047bc6d3..c39959f428 100644 --- a/ext/digest/sha1/sha1init.c +++ b/ext/digest/sha1/sha1init.c @@ -55,9 +55,8 @@ Init_sha1(void) mDigest = rb_define_module("Digest"); /* let rdoc know */ #endif mDigest = rb_digest_namespace(); - cDigest_Base = rb_path2class("Digest::Base"); + cDigest_Base = rb_const_get(mDigest, rb_intern_const("Base")); cDigest_SHA1 = rb_define_class_under(mDigest, "SHA1", cDigest_Base); - rb_iv_set(cDigest_SHA1, "metadata", rb_digest_make_metadata(&sha1)); } diff --git a/ext/digest/sha2/depend b/ext/digest/sha2/depend index 7b88b6411f..2b74776b3e 100644 --- a/ext/digest/sha2/depend +++ b/ext/digest/sha2/depend @@ -131,6 +131,7 @@ sha2.o: $(hdrdir)/ruby/internal/intern/re.h sha2.o: $(hdrdir)/ruby/internal/intern/ruby.h sha2.o: $(hdrdir)/ruby/internal/intern/select.h sha2.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +sha2.o: $(hdrdir)/ruby/internal/intern/set.h sha2.o: $(hdrdir)/ruby/internal/intern/signal.h sha2.o: $(hdrdir)/ruby/internal/intern/sprintf.h sha2.o: $(hdrdir)/ruby/internal/intern/string.h @@ -150,6 +151,7 @@ sha2.o: $(hdrdir)/ruby/internal/special_consts.h sha2.o: $(hdrdir)/ruby/internal/static_assert.h sha2.o: $(hdrdir)/ruby/internal/stdalign.h sha2.o: $(hdrdir)/ruby/internal/stdbool.h +sha2.o: $(hdrdir)/ruby/internal/stdckdint.h sha2.o: $(hdrdir)/ruby/internal/symbol.h sha2.o: $(hdrdir)/ruby/internal/value.h sha2.o: $(hdrdir)/ruby/internal/value_type.h @@ -292,6 +294,7 @@ sha2init.o: $(hdrdir)/ruby/internal/intern/re.h sha2init.o: $(hdrdir)/ruby/internal/intern/ruby.h sha2init.o: $(hdrdir)/ruby/internal/intern/select.h sha2init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +sha2init.o: $(hdrdir)/ruby/internal/intern/set.h sha2init.o: $(hdrdir)/ruby/internal/intern/signal.h sha2init.o: $(hdrdir)/ruby/internal/intern/sprintf.h sha2init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -311,6 +314,7 @@ sha2init.o: $(hdrdir)/ruby/internal/special_consts.h sha2init.o: $(hdrdir)/ruby/internal/static_assert.h sha2init.o: $(hdrdir)/ruby/internal/stdalign.h sha2init.o: $(hdrdir)/ruby/internal/stdbool.h +sha2init.o: $(hdrdir)/ruby/internal/stdckdint.h sha2init.o: $(hdrdir)/ruby/internal/symbol.h sha2init.o: $(hdrdir)/ruby/internal/value.h sha2init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/digest/sha2/sha2cc.h b/ext/digest/sha2/sha2cc.h index 3f99604465..1245a2e2c7 100644 --- a/ext/digest/sha2/sha2cc.h +++ b/ext/digest/sha2/sha2cc.h @@ -1,6 +1,33 @@ #define COMMON_DIGEST_FOR_OPENSSL 1 #include <CommonCrypto/CommonDigest.h> +/* + * Prior to 10.5, OpenSSL-compatible definitions are missing for + * SHA2 macros, though the CC_ versions are present. + * Add the missing definitions we actually use here if needed. + * Note that the definitions are the argless 10.6+-style. + * The weird CTX mismatch is copied from the 10.6 header. + */ +#ifndef SHA256_DIGEST_LENGTH +#define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH +#define SHA256_CTX CC_SHA256_CTX +#define SHA256_Update CC_SHA256_Update +#define SHA256_Final CC_SHA256_Final +#endif /* !defined SHA256_DIGEST_LENGTH */ + +#ifndef SHA384_DIGEST_LENGTH +#define SHA384_DIGEST_LENGTH CC_SHA384_DIGEST_LENGTH +#define SHA512_CTX CC_SHA512_CTX +#define SHA384_Update CC_SHA384_Update +#define SHA384_Final CC_SHA384_Final +#endif /* !defined SHA384_DIGEST_LENGTH */ + +#ifndef SHA512_DIGEST_LENGTH +#define SHA512_DIGEST_LENGTH CC_SHA512_DIGEST_LENGTH +#define SHA512_Update CC_SHA512_Update +#define SHA512_Final CC_SHA512_Final +#endif /* !defined SHA512_DIGEST_LENGTH */ + #define SHA256_BLOCK_LENGTH CC_SHA256_BLOCK_BYTES #define SHA384_BLOCK_LENGTH CC_SHA384_BLOCK_BYTES #define SHA512_BLOCK_LENGTH CC_SHA512_BLOCK_BYTES @@ -29,3 +56,15 @@ static DEFINE_FINISH_FUNC_FROM_FINAL(SHA512) #undef SHA512_Finish #define SHA512_Update rb_digest_SHA512_update #define SHA512_Finish rb_digest_SHA512_finish + +/* + * Pre-10.6 defines are with args, which don't match the argless use in + * the function pointer inits. Thus, we redefine SHA*_Init as well. + * This is a NOP on 10.6+. + */ +#undef SHA256_Init +#define SHA256_Init CC_SHA256_Init +#undef SHA384_Init +#define SHA384_Init CC_SHA384_Init +#undef SHA512_Init +#define SHA512_Init CC_SHA512_Init diff --git a/ext/digest/sha2/sha2init.c b/ext/digest/sha2/sha2init.c index 94cccf3feb..3923e3724c 100644 --- a/ext/digest/sha2/sha2init.c +++ b/ext/digest/sha2/sha2init.c @@ -25,29 +25,50 @@ static const rb_digest_metadata_t sha##bitlen = { \ FOREACH_BITLEN(DEFINE_ALGO_METADATA) /* + * Document-class: Digest::SHA256 < Digest::Base + * * Classes for calculating message digests using the SHA-256/384/512 * Secure Hash Algorithm(s) by NIST (the US' National Institute of * Standards and Technology), described in FIPS PUB 180-2. + * + * See SHA2. + */ +/* + * Document-class: Digest::SHA384 < Digest::Base + * + * Classes for calculating message digests using the SHA-256/384/512 + * Secure Hash Algorithm(s) by NIST (the US' National Institute of + * Standards and Technology), described in FIPS PUB 180-2. + * + * See SHA2. + */ +/* + * Document-class: Digest::SHA512 < Digest::Base + * + * Classes for calculating message digests using the SHA-256/384/512 + * Secure Hash Algorithm(s) by NIST (the US' National Institute of + * Standards and Technology), described in FIPS PUB 180-2. + * + * See SHA2. */ void Init_sha2(void) { - VALUE mDigest, cDigest_Base; + VALUE mDigest, cDigest_Base, cDigest_SHA2; ID id_metadata = rb_id_metadata(); -#define DECLARE_ALGO_CLASS(bitlen) \ - VALUE cDigest_SHA##bitlen; - - FOREACH_BITLEN(DECLARE_ALGO_CLASS) - +#if 0 + mDigest = rb_define_module("Digest"); /* let rdoc know */ +#endif mDigest = rb_digest_namespace(); - cDigest_Base = rb_path2class("Digest::Base"); + cDigest_Base = rb_const_get(mDigest, rb_intern_const("Base")); + + cDigest_SHA2 = rb_define_class_under(mDigest, "SHA256", cDigest_Base); + rb_ivar_set(cDigest_SHA2, id_metadata, rb_digest_make_metadata(&sha256)); -#define DEFINE_ALGO_CLASS(bitlen) \ - cDigest_SHA##bitlen = rb_define_class_under(mDigest, "SHA" #bitlen, cDigest_Base); \ -\ - rb_ivar_set(cDigest_SHA##bitlen, id_metadata, \ - rb_digest_make_metadata(&sha##bitlen)); + cDigest_SHA2 = rb_define_class_under(mDigest, "SHA384", cDigest_Base); + rb_ivar_set(cDigest_SHA2, id_metadata, rb_digest_make_metadata(&sha384)); - FOREACH_BITLEN(DEFINE_ALGO_CLASS) + cDigest_SHA2 = rb_define_class_under(mDigest, "SHA512", cDigest_Base); + rb_ivar_set(cDigest_SHA2, id_metadata, rb_digest_make_metadata(&sha512)); } diff --git a/ext/digest/test.sh b/ext/digest/test.sh deleted file mode 100644 index 328c7575e6..0000000000 --- a/ext/digest/test.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh -# -# $RoughId: test.sh,v 1.5 2001/07/13 15:38:27 knu Exp $ -# $Id$ - -RUBY=${RUBY:=ruby} -MAKE=${MAKE:=make} -CFLAGS=${CFLAGS:=-Wall} - -${RUBY} extconf.rb --with-cflags="${CFLAGS}" -${MAKE} clean -${MAKE} - -for algo in md5 rmd160 sha1 sha2; do - args=--with-cflags="${CFLAGS}" - - if [ $WITH_BUNDLED_ENGINES ]; then - args="$args --with-bundled-$algo" - fi - - (cd $algo && - ${RUBY} extconf.rb $args; - ${MAKE} clean; - ${MAKE}) - ln -sf ../../$algo/$algo.so lib/digest/ -done - -${RUBY} -I. -I./lib ../../test/digest/test_digest.rb - -rm lib/digest/*.so diff --git a/ext/erb/escape/escape.c b/ext/erb/escape/escape.c index 67b2d1ef34..1794fc30eb 100644 --- a/ext/erb/escape/escape.c +++ b/ext/erb/escape/escape.c @@ -39,35 +39,50 @@ static VALUE optimized_escape_html(VALUE str) { VALUE vbuf; - char *buf = ALLOCV_N(char, vbuf, escaped_length(str)); + char *buf = NULL; const char *cstr = RSTRING_PTR(str); const char *end = cstr + RSTRING_LEN(str); - char *dest = buf; + const char *segment_start = cstr; + char *dest = NULL; while (cstr < end) { const unsigned char c = *cstr++; uint8_t len = html_escape_table[c].len; if (len) { + size_t segment_len = cstr - segment_start - 1; + if (!buf) { + buf = ALLOCV_N(char, vbuf, escaped_length(str)); + dest = buf; + } + if (segment_len) { + memcpy(dest, segment_start, segment_len); + dest += segment_len; + } + segment_start = cstr; memcpy(dest, html_escape_table[c].str, len); dest += len; } - else { - *dest++ = c; - } } - VALUE escaped = str; - if (RSTRING_LEN(str) < (dest - buf)) { + if (buf) { + size_t segment_len = cstr - segment_start; + if (segment_len) { + memcpy(dest, segment_start, segment_len); + dest += segment_len; + } escaped = rb_str_new(buf, dest - buf); preserve_original_state(str, escaped); + ALLOCV_END(vbuf); } - ALLOCV_END(vbuf); return escaped; } -// ERB::Util.html_escape is different from CGI.escapeHTML in the following two parts: -// * ERB::Util.html_escape converts an argument with #to_s first (only if it's not T_STRING) -// * ERB::Util.html_escape does not allocate a new string when nothing needs to be escaped +/* + * ERB::Util.html_escape is similar to CGI.escapeHTML but different in the following two parts: + * + * * ERB::Util.html_escape converts an argument with #to_s first (only if it's not T_STRING) + * * ERB::Util.html_escape does not allocate a new string when nothing needs to be escaped + */ static VALUE erb_escape_html(VALUE self, VALUE str) { @@ -86,6 +101,10 @@ erb_escape_html(VALUE self, VALUE str) void Init_escape(void) { +#ifdef HAVE_RB_EXT_RACTOR_SAFE + rb_ext_ractor_safe(true); +#endif + rb_cERB = rb_define_class("ERB", rb_cObject); rb_mEscape = rb_define_module_under(rb_cERB, "Escape"); rb_define_module_function(rb_mEscape, "html_escape", erb_escape_html, 1); diff --git a/ext/erb/escape/extconf.rb b/ext/erb/escape/extconf.rb index 783e8c1f55..b211a9783f 100644 --- a/ext/erb/escape/extconf.rb +++ b/ext/erb/escape/extconf.rb @@ -4,5 +4,6 @@ case RUBY_ENGINE when 'jruby', 'truffleruby' File.write('Makefile', dummy_makefile($srcdir).join) else + have_func("rb_ext_ractor_safe", "ruby.h") create_makefile 'erb/escape' end diff --git a/ext/etc/depend b/ext/etc/depend index 00787b6aaf..77fe56a6bf 100644 --- a/ext/etc/depend +++ b/ext/etc/depend @@ -143,6 +143,7 @@ etc.o: $(hdrdir)/ruby/internal/intern/re.h etc.o: $(hdrdir)/ruby/internal/intern/ruby.h etc.o: $(hdrdir)/ruby/internal/intern/select.h etc.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +etc.o: $(hdrdir)/ruby/internal/intern/set.h etc.o: $(hdrdir)/ruby/internal/intern/signal.h etc.o: $(hdrdir)/ruby/internal/intern/sprintf.h etc.o: $(hdrdir)/ruby/internal/intern/string.h @@ -162,6 +163,7 @@ etc.o: $(hdrdir)/ruby/internal/special_consts.h etc.o: $(hdrdir)/ruby/internal/static_assert.h etc.o: $(hdrdir)/ruby/internal/stdalign.h etc.o: $(hdrdir)/ruby/internal/stdbool.h +etc.o: $(hdrdir)/ruby/internal/stdckdint.h etc.o: $(hdrdir)/ruby/internal/symbol.h etc.o: $(hdrdir)/ruby/internal/value.h etc.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/etc/etc.c b/ext/etc/etc.c index fcbd1af1b5..8d50a96a62 100644 --- a/ext/etc/etc.c +++ b/ext/etc/etc.c @@ -56,7 +56,9 @@ static VALUE sGroup; #endif RUBY_EXTERN char *getlogin(void); -#define RUBY_ETC_VERSION "1.4.3" +#define RUBY_ETC_VERSION "1.4.6" + +#define SYMBOL_LIT(str) ID2SYM(rb_intern_const(str "")) #ifdef HAVE_RB_DEPRECATE_CONSTANT void rb_deprecate_constant(VALUE mod, const char *name); @@ -691,6 +693,16 @@ etc_getgrent(VALUE obj) VALUE rb_w32_special_folder(int type); UINT rb_w32_system_tmpdir(WCHAR *path, UINT len); VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc); +#elif defined(LOAD_RELATIVE) +static inline VALUE +rbconfig(void) +{ + VALUE config; + rb_require("rbconfig"); + config = rb_const_get(rb_path2class("RbConfig"), rb_intern_const("CONFIG")); + Check_Type(config, T_HASH); + return config; +} #endif /* call-seq: @@ -710,6 +722,8 @@ etc_sysconfdir(VALUE obj) { #ifdef _WIN32 return rb_w32_special_folder(CSIDL_COMMON_APPDATA); +#elif defined(LOAD_RELATIVE) + return rb_hash_aref(rbconfig(), rb_str_new_lit("sysconfdir")); #else return rb_filesystem_str_new_cstr(SYSCONFDIR); #endif @@ -811,18 +825,14 @@ etc_uname(VALUE obj) sysname = "Windows"; break; } - rb_hash_aset(result, ID2SYM(rb_intern("sysname")), rb_str_new_cstr(sysname)); + rb_hash_aset(result, SYMBOL_LIT("sysname"), rb_str_new_cstr(sysname)); release = rb_sprintf("%lu.%lu.%lu", v.dwMajorVersion, v.dwMinorVersion, v.dwBuildNumber); - rb_hash_aset(result, ID2SYM(rb_intern("release")), release); + rb_hash_aset(result, SYMBOL_LIT("release"), release); version = rb_sprintf("%s Version %"PRIsVALUE": %"PRIsVALUE, sysname, release, rb_w32_conv_from_wchar(v.szCSDVersion, rb_utf8_encoding())); - rb_hash_aset(result, ID2SYM(rb_intern("version")), version); + rb_hash_aset(result, SYMBOL_LIT("version"), version); -# if defined _MSC_VER && _MSC_VER < 1300 -# define GET_COMPUTER_NAME(ptr, plen) GetComputerNameW(ptr, plen) -# else # define GET_COMPUTER_NAME(ptr, plen) GetComputerNameExW(ComputerNameDnsFullyQualified, ptr, plen) -# endif GET_COMPUTER_NAME(NULL, &len); buf = ALLOCV_N(WCHAR, vbuf, len); if (GET_COMPUTER_NAME(buf, &len)) { @@ -830,7 +840,7 @@ etc_uname(VALUE obj) } ALLOCV_END(vbuf); if (NIL_P(nodename)) nodename = rb_str_new(0, 0); - rb_hash_aset(result, ID2SYM(rb_intern("nodename")), nodename); + rb_hash_aset(result, SYMBOL_LIT("nodename"), nodename); # ifndef PROCESSOR_ARCHITECTURE_AMD64 # define PROCESSOR_ARCHITECTURE_AMD64 9 @@ -854,7 +864,7 @@ etc_uname(VALUE obj) break; } - rb_hash_aset(result, ID2SYM(rb_intern("machine")), rb_str_new_cstr(mach)); + rb_hash_aset(result, SYMBOL_LIT("machine"), rb_str_new_cstr(mach)); #else struct utsname u; int ret; @@ -865,11 +875,11 @@ etc_uname(VALUE obj) rb_sys_fail("uname"); result = rb_hash_new(); - rb_hash_aset(result, ID2SYM(rb_intern("sysname")), rb_str_new_cstr(u.sysname)); - rb_hash_aset(result, ID2SYM(rb_intern("nodename")), rb_str_new_cstr(u.nodename)); - rb_hash_aset(result, ID2SYM(rb_intern("release")), rb_str_new_cstr(u.release)); - rb_hash_aset(result, ID2SYM(rb_intern("version")), rb_str_new_cstr(u.version)); - rb_hash_aset(result, ID2SYM(rb_intern("machine")), rb_str_new_cstr(u.machine)); + rb_hash_aset(result, SYMBOL_LIT("sysname"), rb_str_new_cstr(u.sysname)); + rb_hash_aset(result, SYMBOL_LIT("nodename"), rb_str_new_cstr(u.nodename)); + rb_hash_aset(result, SYMBOL_LIT("release"), rb_str_new_cstr(u.release)); + rb_hash_aset(result, SYMBOL_LIT("version"), rb_str_new_cstr(u.version)); + rb_hash_aset(result, SYMBOL_LIT("machine"), rb_str_new_cstr(u.machine)); #endif return result; @@ -1149,14 +1159,26 @@ Init_etc(void) { VALUE mEtc; -#ifdef HAVE_RB_EXT_RACTOR_SAFE - RB_EXT_RACTOR_SAFE(true); -#endif mEtc = rb_define_module("Etc"); /* The version */ rb_define_const(mEtc, "VERSION", rb_str_new_cstr(RUBY_ETC_VERSION)); init_constants(mEtc); + /* Ractor-safe methods */ +#ifdef HAVE_RB_EXT_RACTOR_SAFE + RB_EXT_RACTOR_SAFE(true); +#endif + rb_define_module_function(mEtc, "systmpdir", etc_systmpdir, 0); + rb_define_module_function(mEtc, "uname", etc_uname, 0); + rb_define_module_function(mEtc, "sysconf", etc_sysconf, 1); + rb_define_module_function(mEtc, "confstr", etc_confstr, 1); + rb_define_method(rb_cIO, "pathconf", io_pathconf, 1); + rb_define_module_function(mEtc, "nprocessors", etc_nprocessors, 0); + + /* Non-Ractor-safe methods, see https://bugs.ruby-lang.org/issues/21115 */ +#ifdef HAVE_RB_EXT_RACTOR_SAFE + RB_EXT_RACTOR_SAFE(false); +#endif rb_define_module_function(mEtc, "getlogin", etc_getlogin, 0); rb_define_module_function(mEtc, "getpwuid", etc_getpwuid, -1); @@ -1172,13 +1194,9 @@ Init_etc(void) rb_define_module_function(mEtc, "setgrent", etc_setgrent, 0); rb_define_module_function(mEtc, "endgrent", etc_endgrent, 0); rb_define_module_function(mEtc, "getgrent", etc_getgrent, 0); + + /* Uses RbConfig::CONFIG so does not work in a Ractor */ rb_define_module_function(mEtc, "sysconfdir", etc_sysconfdir, 0); - rb_define_module_function(mEtc, "systmpdir", etc_systmpdir, 0); - rb_define_module_function(mEtc, "uname", etc_uname, 0); - rb_define_module_function(mEtc, "sysconf", etc_sysconf, 1); - rb_define_module_function(mEtc, "confstr", etc_confstr, 1); - rb_define_method(rb_cIO, "pathconf", io_pathconf, 1); - rb_define_module_function(mEtc, "nprocessors", etc_nprocessors, 0); sPasswd = rb_struct_define_under(mEtc, "Passwd", "name", @@ -1289,4 +1307,8 @@ Init_etc(void) rb_extend_object(sGroup, rb_mEnumerable); rb_define_singleton_method(sGroup, "each", etc_each_group, 0); #endif + +#if defined(HAVE_GETPWENT) || defined(HAVE_GETGRENT) + (void)safe_setup_str; +#endif } diff --git a/ext/etc/etc.gemspec b/ext/etc/etc.gemspec index 98c6d66faa..0e9803dc62 100644 --- a/ext/etc/etc.gemspec +++ b/ext/etc/etc.gemspec @@ -24,7 +24,8 @@ Gem::Specification.new do |spec| changelogs = Dir.glob("logs/ChangeLog-[1-9]*[^~]", base: __dir__) spec.files = %w[ - LICENSE.txt + BSDL + COPYING README.md ChangeLog ext/etc/constdefs.h @@ -39,5 +40,5 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.extensions = %w{ext/etc/extconf.rb} - spec.required_ruby_version = ">= 2.6.0" + spec.required_ruby_version = ">= 2.7.0" end diff --git a/ext/etc/extconf.rb b/ext/etc/extconf.rb index 2e28d58037..497303a4fa 100644 --- a/ext/etc/extconf.rb +++ b/ext/etc/extconf.rb @@ -10,8 +10,30 @@ headers = [] have_library("sun", "getpwnam") # NIS (== YP) interface for IRIX 4 have_func("uname((struct utsname *)NULL)", headers) have_func("getlogin") -have_func("getpwent") -have_func("getgrent") +if have_func("getpwent") + have_struct_member('struct passwd', 'pw_gecos', 'pwd.h') + have_struct_member('struct passwd', 'pw_change', 'pwd.h') + have_struct_member('struct passwd', 'pw_quota', 'pwd.h') + if have_struct_member('struct passwd', 'pw_age', 'pwd.h') + case what_type?('struct passwd', 'pw_age', 'pwd.h') + when "string" + f = "safe_setup_str" + when "long long" + f = "LL2NUM" + else + f = "INT2NUM" + end + $defs.push("-DPW_AGE2VAL="+f) + end + have_struct_member('struct passwd', 'pw_class', 'pwd.h') + have_struct_member('struct passwd', 'pw_comment', 'pwd.h') unless /cygwin/ === RUBY_PLATFORM + have_struct_member('struct passwd', 'pw_expire', 'pwd.h') + have_struct_member('struct passwd', 'pw_passwd', 'pwd.h') +end +if have_func("getgrent") + have_struct_member('struct group', 'gr_passwd', 'grp.h') +end + if (sysconfdir = RbConfig::CONFIG["sysconfdir"] and !RbConfig.expand(sysconfdir.dup, "prefix"=>"", "DESTDIR"=>"").empty?) $defs.push("-DSYSCONFDIR=#{Shellwords.escape(sysconfdir.dump)}") @@ -21,26 +43,6 @@ have_func("sysconf") have_func("confstr") have_func("fpathconf") -have_struct_member('struct passwd', 'pw_gecos', 'pwd.h') -have_struct_member('struct passwd', 'pw_change', 'pwd.h') -have_struct_member('struct passwd', 'pw_quota', 'pwd.h') -if have_struct_member('struct passwd', 'pw_age', 'pwd.h') - case what_type?('struct passwd', 'pw_age', 'pwd.h') - when "string" - f = "safe_setup_str" - when "long long" - f = "LL2NUM" - else - f = "INT2NUM" - end - $defs.push("-DPW_AGE2VAL="+f) -end -have_struct_member('struct passwd', 'pw_class', 'pwd.h') -have_struct_member('struct passwd', 'pw_comment', 'pwd.h') unless /cygwin/ === RUBY_PLATFORM -have_struct_member('struct passwd', 'pw_expire', 'pwd.h') -have_struct_member('struct passwd', 'pw_passwd', 'pwd.h') -have_struct_member('struct group', 'gr_passwd', 'grp.h') - # for https://github.com/ruby/etc srcdir = File.expand_path("..", __FILE__) constdefs = "#{srcdir}/constdefs.h" @@ -58,7 +60,7 @@ end # TODO: remove when dropping 2.7 support, as exported since 3.0 have_func('rb_deprecate_constant(Qnil, "None")') -have_func("rb_io_descriptor") +have_func("rb_io_descriptor", "ruby/io.h") $distcleanfiles << "constdefs.h" diff --git a/ext/etc/mkconstants.rb b/ext/etc/mkconstants.rb index c8ebb67767..a766560a8a 100644 --- a/ext/etc/mkconstants.rb +++ b/ext/etc/mkconstants.rb @@ -79,15 +79,11 @@ def each_name(pat) } end -erb_new = lambda do |src, safe, trim| - if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+ - ERB.new(src, trim_mode: trim) - else - ERB.new(src, safe, trim) - end +erb_new = lambda do |src, trim| + ERB.new(src, trim_mode: trim) end -erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_decls") +erb_new.call(<<'EOS', '%').def_method(Object, "gen_const_decls") % each_const {|name, default_value| #if !defined(<%=name%>) # if defined(HAVE_CONST_<%=name.upcase%>) @@ -101,7 +97,7 @@ erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_decls") % } EOS -erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_defs") +erb_new.call(<<'EOS', '%').def_method(Object, "gen_const_defs") % each_const {|name, default_value| #if defined(<%=name%>) % if comment = COMMENTS[name] @@ -112,13 +108,13 @@ erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_defs") % } EOS -header_result = erb_new.call(<<'EOS', nil, '%').result(binding) +header_result = erb_new.call(<<'EOS', '%').result(binding) /* autogenerated file */ <%= gen_const_decls %> EOS -result = erb_new.call(<<'EOS', nil, '%').result(binding) +result = erb_new.call(<<'EOS', '%').result(binding) /* autogenerated file */ #ifdef HAVE_LONG_LONG diff --git a/ext/extmk.rb b/ext/extmk.rb index 428ffc91a6..578e1cfa01 100755 --- a/ext/extmk.rb +++ b/ext/extmk.rb @@ -2,7 +2,13 @@ # -*- mode: ruby; coding: us-ascii -*- # frozen_string_literal: false -module Gem; end # only needs Gem::Platform +module Gem + # Used by Gem::Platform.local + def self.target_rbconfig + RbConfig::CONFIG + end +end +# only needs Gem::Platform require 'rubygems/platform' # :stopdoc: @@ -37,6 +43,7 @@ require 'rbconfig' $topdir = "." $top_srcdir = srcdir +$extmk = true inplace = File.identical?($top_srcdir, $topdir) $" << "mkmf.rb" @@ -104,7 +111,7 @@ def extract_makefile(makefile, keep = true) end return false end - srcs = Dir[File.join($srcdir, "*.{#{SRC_EXT.join(%q{,})}}")].map {|fn| File.basename(fn)}.sort + srcs = Dir[*SRC_EXT.map {|e| "*.#{e}"}, base: $srcdir].map {|fn| File.basename(fn)}.sort if !srcs.empty? old_srcs = m[/^ORIG_SRCS[ \t]*=[ \t](.*)/, 1] or return false (old_srcs.split - srcs).empty? or return false @@ -159,8 +166,6 @@ def extmake(target, basedir = 'ext', maybestatic = true) $mdir = target $srcdir = File.join($top_srcdir, basedir, $mdir) $preload = nil - $objs = [] - $srcs = [] $extso = [] makefile = "./Makefile" static = $static @@ -194,7 +199,7 @@ def extmake(target, basedir = 'ext', maybestatic = true) begin $extconf_h = nil ok &&= extract_makefile(makefile) - old_objs = $objs + old_objs = $objs || [] old_cleanfiles = $distcleanfiles | $cleanfiles conf = ["#{$srcdir}/makefile.rb", "#{$srcdir}/extconf.rb"].find {|f| File.exist?(f)} if (!ok || ($extconf_h && !File.exist?($extconf_h)) || @@ -257,6 +262,8 @@ def extmake(target, basedir = 'ext', maybestatic = true) unless $destdir.to_s.empty? or $mflags.defined?("DESTDIR") args += ["DESTDIR=" + relative_from($destdir, "../"+prefix)] end + $objs ||= [] + $srcs ||= [] if $static and ok and !$objs.empty? and !noinstall args += ["static"] $extlist.push [(maybestatic ? $static : false), target, $target, $preload] @@ -472,12 +479,13 @@ if exts = ARGV.shift $extension = [exts] if exts if ext_prefix.start_with?('.') @gemname = exts - elsif exts - $static_ext.delete_if {|t, *| !File.fnmatch(t, exts)} + exts = [] + else + exts &&= $static_ext.select {|t, *| File.fnmatch(t, exts)} end end -ext_prefix = "#{$top_srcdir}/#{ext_prefix || 'ext'}" -exts = $static_ext.sort_by {|t, i| i}.collect {|t, i| t} +ext_prefix ||= 'ext' +exts = (exts || $static_ext).sort_by {|t, i| i}.collect {|t, i| t} default_exclude_exts = case when $cygwin @@ -490,28 +498,30 @@ default_exclude_exts = mandatory_exts = {} withes, withouts = [["--with", nil], ["--without", default_exclude_exts]].collect {|w, d| if !(w = %w[-extensions -ext].collect {|o|arg_config(w+o)}).any? - d ? proc {|c1| d.any?(&c1)} : proc {true} + d ? proc {|&c1| d.any?(&c1)} : proc {true} elsif (w = w.grep(String)).empty? proc {true} else w = w.collect {|o| o.split(/,/)}.flatten w.collect! {|o| o == '+' ? d : o}.flatten! - proc {|c1| w.any?(&c1)} + proc {|&c1| w.any?(&c1)} end } cond = proc {|ext, *| - withes.call(proc {|n| - !n or (mandatory_exts[ext] = true if File.fnmatch(n, ext)) - }) and - !withouts.call(proc {|n| File.fnmatch(n, ext)}) + withes.call {|n| !n or (mandatory_exts[ext] = true if File.fnmatch(n, ext))} and + !withouts.call {|n| File.fnmatch(n, ext)} } ($extension || %w[*]).each do |e| e = e.sub(/\A(?:\.\/)+/, '') - incl, excl = Dir.glob("#{ext_prefix}/#{e}/**/extconf.rb").collect {|d| - d = File.dirname(d) - d.slice!(0, ext_prefix.length + 1) - d + incl, excl = Dir.glob("#{e}/**/extconf.rb", base: "#$top_srcdir/#{ext_prefix}").collect {|d| + File.dirname(d) }.partition {|ext| + if @gemname + ext = ext[%r[\A[^/]+]] # extract gem name + Dir.glob("*.gemspec", base: "#$top_srcdir/#{ext_prefix}/#{ext}") do |g| + break ext = g if ext.start_with?("#{g.chomp!(".gemspec")}-") + end + end with_config(ext, &cond) } incl.sort! @@ -522,7 +532,7 @@ cond = proc {|ext, *| exts.delete_if {|d| File.fnmatch?("-*", d)} end end -ext_prefix = ext_prefix[$top_srcdir.size+1..-2] +ext_prefix.chomp!("/") @ext_prefix = ext_prefix @inplace = inplace @@ -545,7 +555,14 @@ extend Module.new { end def create_makefile(*args, &block) - return super unless @gemname + unless @gemname + if $static and (target = args.first).include?("/") + base = File.basename(target) + $defs << "-DInit_#{base}=Init_#{target.tr('/', '_')}" + $defs << "-DInitVM_#{base}=InitVM_#{target.tr('/', '_')}" + end + return super + end super(*args) do |conf| conf.find do |s| s.sub!(%r(^(srcdir *= *)\$\(top_srcdir\)/\.bundle/gems/[^/]+(?=/))) { @@ -561,7 +578,8 @@ extend Module.new { } end - gemlib = File.directory?("#{$top_srcdir}/#{@ext_prefix}/#{@gemname}/lib") + conf = yield conf if block + if conf.any? {|s| /^TARGET *= *\S/ =~ s} conf << %{ gem_platform = #{Gem::Platform.local} @@ -574,6 +592,7 @@ gem = #{@gemname} build_complete = $(TARGET_GEM_DIR)/gem.build_complete install-so: build_complete clean-so:: clean-build_complete +$(build_complete) $(OBJS): $(TARGET_SO_DIR_TIMESTAMP) build_complete: $(build_complete) $(build_complete): $(TARGET_SO) @@ -594,23 +613,25 @@ gemspec: $(gemspec) clean-gemspec: -$(Q)$(RM) $(gemspec) -} - - if gemlib - conf << %{ -install-rb: gemlib -clean-rb:: clean-gemlib LN_S = #{config_string('LN_S')} CP_R = #{config_string('CP')} -r - -gemlib = $(TARGET_TOPDIR)/gems/$(gem)/lib -gemlib:#{%{ $(gemlib)\n$(gemlib): $(gem_srcdir)/lib} if $nmake} - $(Q) #{@inplace ? '$(NULLCMD) ' : ''}$(RUBY) $(top_srcdir)/tool/ln_sr.rb -q -f -T $(gem_srcdir)/lib $(gemlib) - -clean-gemlib: - $(Q) $(#{@inplace ? 'NULLCMD' : 'RM_RF'}) $(gemlib) } + unless @inplace + %w[bin lib].each do |d| + next unless File.directory?("#{$top_srcdir}/#{@ext_prefix}/#{@gemname}/#{d}") + conf << %{ +install-rb: gem#{d} +clean-rb:: clean-gem#{d} + +gem#{d} = $(TARGET_TOPDIR)/gems/$(gem)/#{d} +gem#{d}:#{%{ $(gem#{d})\n$(gem#{d}): $(gem_srcdir)/#{d}} if $nmake} + $(Q) $(RUBY) $(top_srcdir)/tool/ln_sr.rb -q -f -T $(gem_srcdir)/#{d} $(gem#{d}) + +clean-gem#{d}: + $(Q) $(RM_RF) $(gem#{d}) +} + end end end @@ -628,7 +649,9 @@ $hdrdir = ($top_srcdir = relative_from(srcdir, $topdir = "..")) + "/include" extso = [] fails = [] exts.each do |d| - $static = $force_static ? true : $static_ext[d] + $static = $force_static ? true : $static_ext.fetch(d) do + $static_ext.any? {|t, | File.fnmatch?(t, d)} + end if !$nodynamic or $static result = extmake(d, ext_prefix, !@gemname) or abort @@ -754,7 +777,6 @@ begin end submakeopts << 'EXTLDFLAGS="$(EXTLDFLAGS)"' submakeopts << 'EXTINITS="$(EXTINITS)"' - submakeopts << 'UPDATE_LIBRARIES="$(UPDATE_LIBRARIES)"' submakeopts << 'SHOWFLAGS=' mf.macro "SUBMAKEOPTS", submakeopts mf.macro "NOTE_MESG", %w[$(RUBY) $(top_srcdir)/tool/lib/colorize.rb skip] @@ -793,9 +815,9 @@ begin if $gnumake == "yes" submake = "$(MAKE) -C $(@D)" else - submake = "cd $(@D) && " - config_string("exec") {|str| submake << str << " "} - submake << "$(MAKE)" + submake = ["cd", (sep ? "$(@D:/=#{sep})" : "$(@D)"), "&&"] + config_string("exec") {|str| submake << str} + submake = (submake << "$(MAKE)").join(" ") end targets.each do |tgt| exts.each do |d| @@ -810,7 +832,7 @@ begin end mf.puts "#{t}:#{pd}\n\t$(Q)#{submake} $(MFLAGS) V=$(V) $(@F)" if clean and clean.begin(1) - mf.puts "\t$(Q)$(RM) $(ext_build_dir)/exts.mk\n\t$(Q)$(RMDIRS) -p $(@D)" + mf.puts "\t$(Q)$(RM) $(ext_build_dir)/exts.mk\n\t$(Q)$(RMDIRS) $(@D)" end end end diff --git a/ext/fcntl/depend b/ext/fcntl/depend index 9ce9fa30ef..57ea0f2106 100644 --- a/ext/fcntl/depend +++ b/ext/fcntl/depend @@ -128,6 +128,7 @@ fcntl.o: $(hdrdir)/ruby/internal/intern/re.h fcntl.o: $(hdrdir)/ruby/internal/intern/ruby.h fcntl.o: $(hdrdir)/ruby/internal/intern/select.h fcntl.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +fcntl.o: $(hdrdir)/ruby/internal/intern/set.h fcntl.o: $(hdrdir)/ruby/internal/intern/signal.h fcntl.o: $(hdrdir)/ruby/internal/intern/sprintf.h fcntl.o: $(hdrdir)/ruby/internal/intern/string.h @@ -147,6 +148,7 @@ fcntl.o: $(hdrdir)/ruby/internal/special_consts.h fcntl.o: $(hdrdir)/ruby/internal/static_assert.h fcntl.o: $(hdrdir)/ruby/internal/stdalign.h fcntl.o: $(hdrdir)/ruby/internal/stdbool.h +fcntl.o: $(hdrdir)/ruby/internal/stdckdint.h fcntl.o: $(hdrdir)/ruby/internal/symbol.h fcntl.o: $(hdrdir)/ruby/internal/value.h fcntl.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/fcntl/fcntl.c b/ext/fcntl/fcntl.c index 4af2077a0a..b987e237dd 100644 --- a/ext/fcntl/fcntl.c +++ b/ext/fcntl/fcntl.c @@ -28,7 +28,10 @@ pack up your own arguments to pass as args for locking functions, etc. #include "ruby.h" #include <fcntl.h> -/* Fcntl loads the constants defined in the system's <fcntl.h> C header +/* + * Document-module: Fcntl + * + * Fcntl loads the constants defined in the system's <fcntl.h> C header * file, and used with both the fcntl(2) and open(2) POSIX system calls. * * To perform a fcntl(2) operation, use IO::fcntl. @@ -62,18 +65,18 @@ pack up your own arguments to pass as args for locking functions, etc. * */ -#define FCNTL_VERSION "1.1.0" +#define FCNTL_VERSION "1.3.0" void Init_fcntl(void) { VALUE mFcntl = rb_define_module("Fcntl"); + /* The version string. */ rb_define_const(mFcntl, "VERSION", rb_str_new_cstr(FCNTL_VERSION)); #ifdef F_DUPFD - /* Document-const: F_DUPFD - * + /* * Duplicate a file descriptor to the minimum unused file descriptor * greater than or equal to the argument. * @@ -84,199 +87,214 @@ Init_fcntl(void) rb_define_const(mFcntl, "F_DUPFD", INT2NUM(F_DUPFD)); #endif #ifdef F_GETFD - /* Document-const: F_GETFD - * + /* * Read the close-on-exec flag of a file descriptor. */ rb_define_const(mFcntl, "F_GETFD", INT2NUM(F_GETFD)); #endif #ifdef F_GETLK - /* Document-const: F_GETLK - * + /* * Determine whether a given region of a file is locked. This uses one of * the F_*LK flags. */ rb_define_const(mFcntl, "F_GETLK", INT2NUM(F_GETLK)); #endif #ifdef F_SETFD - /* Document-const: F_SETFD - * + /* * Set the close-on-exec flag of a file descriptor. */ rb_define_const(mFcntl, "F_SETFD", INT2NUM(F_SETFD)); #endif #ifdef F_GETFL - /* Document-const: F_GETFL - * + /* * Get the file descriptor flags. This will be one or more of the O_* * flags. */ rb_define_const(mFcntl, "F_GETFL", INT2NUM(F_GETFL)); #endif #ifdef F_SETFL - /* Document-const: F_SETFL - * + /* * Set the file descriptor flags. This will be one or more of the O_* * flags. */ rb_define_const(mFcntl, "F_SETFL", INT2NUM(F_SETFL)); #endif #ifdef F_SETLK - /* Document-const: F_SETLK - * + /* * Acquire a lock on a region of a file. This uses one of the F_*LCK * flags. */ rb_define_const(mFcntl, "F_SETLK", INT2NUM(F_SETLK)); #endif #ifdef F_SETLKW - /* Document-const: F_SETLKW - * + /* * Acquire a lock on a region of a file, waiting if necessary. This uses * one of the F_*LCK flags */ rb_define_const(mFcntl, "F_SETLKW", INT2NUM(F_SETLKW)); #endif #ifdef FD_CLOEXEC - /* Document-const: FD_CLOEXEC - * + /* * the value of the close-on-exec flag. */ rb_define_const(mFcntl, "FD_CLOEXEC", INT2NUM(FD_CLOEXEC)); #endif #ifdef F_RDLCK - /* Document-const: F_RDLCK - * + /* * Read lock for a region of a file */ rb_define_const(mFcntl, "F_RDLCK", INT2NUM(F_RDLCK)); #endif #ifdef F_UNLCK - /* Document-const: F_UNLCK - * + /* * Remove lock for a region of a file */ rb_define_const(mFcntl, "F_UNLCK", INT2NUM(F_UNLCK)); #endif #ifdef F_WRLCK - /* Document-const: F_WRLCK - * + /* * Write lock for a region of a file */ rb_define_const(mFcntl, "F_WRLCK", INT2NUM(F_WRLCK)); #endif #ifdef F_SETPIPE_SZ - /* Document-const: F_SETPIPE_SZ - * + /* * Change the capacity of the pipe referred to by fd to be at least arg bytes. */ rb_define_const(mFcntl, "F_SETPIPE_SZ", INT2NUM(F_SETPIPE_SZ)); #endif #ifdef F_GETPIPE_SZ - /* Document-const: F_GETPIPE_SZ - * + /* * Return (as the function result) the capacity of the pipe referred to by fd. */ rb_define_const(mFcntl, "F_GETPIPE_SZ", INT2NUM(F_GETPIPE_SZ)); #endif #ifdef O_CREAT - /* Document-const: O_CREAT - * + /* * Create the file if it doesn't exist */ rb_define_const(mFcntl, "O_CREAT", INT2NUM(O_CREAT)); #endif #ifdef O_EXCL - /* Document-const: O_EXCL - * + /* * Used with O_CREAT, fail if the file exists */ rb_define_const(mFcntl, "O_EXCL", INT2NUM(O_EXCL)); #endif #ifdef O_NOCTTY - /* Document-const: O_NOCTTY - * + /* * Open TTY without it becoming the controlling TTY */ rb_define_const(mFcntl, "O_NOCTTY", INT2NUM(O_NOCTTY)); #endif #ifdef O_TRUNC - /* Document-const: O_TRUNC - * + /* * Truncate the file on open */ rb_define_const(mFcntl, "O_TRUNC", INT2NUM(O_TRUNC)); #endif #ifdef O_APPEND - /* Document-const: O_APPEND - * + /* * Open the file in append mode */ rb_define_const(mFcntl, "O_APPEND", INT2NUM(O_APPEND)); #endif #ifdef O_NONBLOCK - /* Document-const: O_NONBLOCK - * + /* * Open the file in non-blocking mode */ rb_define_const(mFcntl, "O_NONBLOCK", INT2NUM(O_NONBLOCK)); #endif #ifdef O_NDELAY - /* Document-const: O_NDELAY - * + /* * Open the file in non-blocking mode */ rb_define_const(mFcntl, "O_NDELAY", INT2NUM(O_NDELAY)); #endif #ifdef O_RDONLY - /* Document-const: O_RDONLY - * + /* * Open the file in read-only mode */ rb_define_const(mFcntl, "O_RDONLY", INT2NUM(O_RDONLY)); #endif #ifdef O_RDWR - /* Document-const: O_RDWR - * + /* * Open the file in read-write mode */ rb_define_const(mFcntl, "O_RDWR", INT2NUM(O_RDWR)); #endif #ifdef O_WRONLY - /* Document-const: O_WRONLY - * + /* * Open the file in write-only mode. */ rb_define_const(mFcntl, "O_WRONLY", INT2NUM(O_WRONLY)); #endif -#ifdef O_ACCMODE - /* Document-const: O_ACCMODE - * +#ifndef O_ACCMODE + int O_ACCMODE = (O_RDONLY | O_WRONLY | O_RDWR); +#endif + /* * Mask to extract the read/write flags */ rb_define_const(mFcntl, "O_ACCMODE", INT2FIX(O_ACCMODE)); -#else - /* Document-const: O_ACCMODE - * - * Mask to extract the read/write flags - */ - rb_define_const(mFcntl, "O_ACCMODE", INT2FIX(O_RDONLY | O_WRONLY | O_RDWR)); -#endif #ifdef F_DUP2FD - /* Document-const: F_DUP2FD - * + /* * It is a FreeBSD specific constant and equivalent * to dup2 call. */ rb_define_const(mFcntl, "F_DUP2FD", INT2NUM(F_DUP2FD)); #endif #ifdef F_DUP2FD_CLOEXEC - /* Document-const: F_DUP2FD_CLOEXEC - * + /* * It is a FreeBSD specific constant and acts * similarly as F_DUP2FD but set the FD_CLOEXEC * flag in addition. */ rb_define_const(mFcntl, "F_DUP2FD_CLOEXEC", INT2NUM(F_DUP2FD_CLOEXEC)); #endif + +#ifdef F_PREALLOCATE + /* + * macOS specific flag used for preallocating file space. + */ + rb_define_const(mFcntl, "F_PREALLOCATE", INT2NUM(F_PREALLOCATE)); +#endif + +#ifdef F_ALLOCATECONTIG + /* + * macOS specific flag used with F_PREALLOCATE for allocating contiguous + * space. + */ + rb_define_const(mFcntl, "F_ALLOCATECONTIG", INT2NUM(F_ALLOCATECONTIG)); +#endif + +#ifdef F_ALLOCATEALL + /* + * macOS specific flag used with F_PREALLOCATE for allocating all contiguous + * space or no space. + */ + rb_define_const(mFcntl, "F_ALLOCATEALL", INT2NUM(F_ALLOCATEALL)); +#endif + +#ifdef F_ALLOCATEPERSIST + /* + * macOS specific flag used with F_PREALLOCATE. Allocate space that is not + * freed when close is called. + */ + rb_define_const(mFcntl, "F_ALLOCATEPERSIST", INT2NUM(F_ALLOCATEPERSIST)); +#endif + +#ifdef F_PEOFPOSMODE + /* + * macOS specific flag used with F_PREALLOCATE. Allocate from the physical + * end of file + */ + rb_define_const(mFcntl, "F_PEOFPOSMODE", INT2NUM(F_PEOFPOSMODE)); +#endif + +#ifdef F_VOLPOSMODE + /* + * macOS specific flag used with F_PREALLOCATE. Allocate from the volume offset. + */ + rb_define_const(mFcntl, "F_VOLPOSMODE", INT2NUM(F_VOLPOSMODE)); +#endif } diff --git a/ext/fcntl/fcntl.gemspec b/ext/fcntl/fcntl.gemspec index 54aadb4b81..613a90d1ae 100644 --- a/ext/fcntl/fcntl.gemspec +++ b/ext/fcntl/fcntl.gemspec @@ -23,9 +23,10 @@ Gem::Specification.new do |spec| spec.licenses = ["Ruby", "BSD-2-Clause"] spec.files = ["ext/fcntl/extconf.rb", "ext/fcntl/fcntl.c"] + spec.extra_rdoc_files = [".document", ".rdoc_options", "BSDL", "COPYING", "README.md"] spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] spec.extensions = "ext/fcntl/extconf.rb" - spec.required_ruby_version = ">= 2.3.0" + spec.required_ruby_version = ">= 2.5.0" end diff --git a/ext/fiddle/closure.c b/ext/fiddle/closure.c deleted file mode 100644 index 2b4cdb6608..0000000000 --- a/ext/fiddle/closure.c +++ /dev/null @@ -1,458 +0,0 @@ -#include <fiddle.h> -#include <stdbool.h> -#include <ruby/thread.h> - -int ruby_thread_has_gvl_p(void); /* from internal.h */ - -VALUE cFiddleClosure; - -typedef struct { - void * code; - ffi_closure *pcl; - ffi_cif cif; - int argc; - ffi_type **argv; -} fiddle_closure; - -#if defined(__OpenBSD__) -# define USE_FFI_CLOSURE_ALLOC 0 -#endif - -#if defined(USE_FFI_CLOSURE_ALLOC) -#elif !defined(HAVE_FFI_CLOSURE_ALLOC) -# define USE_FFI_CLOSURE_ALLOC 0 -#else -# define USE_FFI_CLOSURE_ALLOC 1 -#endif - -static void -dealloc(void * ptr) -{ - fiddle_closure * cls = (fiddle_closure *)ptr; -#if USE_FFI_CLOSURE_ALLOC - ffi_closure_free(cls->pcl); -#else - munmap(cls->pcl, sizeof(*cls->pcl)); -#endif - if (cls->argv) xfree(cls->argv); - xfree(cls); -} - -static size_t -closure_memsize(const void * ptr) -{ - fiddle_closure * cls = (fiddle_closure *)ptr; - size_t size = 0; - - size += sizeof(*cls); -#if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API - size += ffi_raw_size(&cls->cif); -#endif - size += sizeof(*cls->argv); - size += sizeof(ffi_closure); - - return size; -} - -const rb_data_type_t closure_data_type = { - .wrap_struct_name = "fiddle/closure", - .function = { - .dmark = 0, - .dfree = dealloc, - .dsize = closure_memsize - }, - .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, -}; - -struct callback_args { - ffi_cif *cif; - void *resp; - void **args; - void *ctx; -}; - -static void * -with_gvl_callback(void *ptr) -{ - struct callback_args *x = ptr; - - VALUE self = (VALUE)x->ctx; - VALUE rbargs = rb_iv_get(self, "@args"); - VALUE ctype = rb_iv_get(self, "@ctype"); - int argc = RARRAY_LENINT(rbargs); - VALUE params = rb_ary_tmp_new(argc); - VALUE ret; - VALUE cPointer; - int i, type; - - cPointer = rb_const_get(mFiddle, rb_intern("Pointer")); - - for (i = 0; i < argc; i++) { - type = NUM2INT(RARRAY_AREF(rbargs, i)); - switch (type) { - case TYPE_VOID: - argc = 0; - break; - case TYPE_INT: - rb_ary_push(params, INT2NUM(*(int *)x->args[i])); - break; - case TYPE_UINT: - rb_ary_push(params, UINT2NUM(*(unsigned int *)x->args[i])); - break; - case TYPE_VOIDP: - rb_ary_push(params, - rb_funcall(cPointer, rb_intern("[]"), 1, - PTR2NUM(*(void **)x->args[i]))); - break; - case TYPE_LONG: - rb_ary_push(params, LONG2NUM(*(long *)x->args[i])); - break; - case TYPE_ULONG: - rb_ary_push(params, ULONG2NUM(*(unsigned long *)x->args[i])); - break; - case TYPE_CHAR: - rb_ary_push(params, INT2NUM(*(signed char *)x->args[i])); - break; - case TYPE_UCHAR: - rb_ary_push(params, UINT2NUM(*(unsigned char *)x->args[i])); - break; - case TYPE_SHORT: - rb_ary_push(params, INT2NUM(*(signed short *)x->args[i])); - break; - case TYPE_USHORT: - rb_ary_push(params, UINT2NUM(*(unsigned short *)x->args[i])); - break; - case TYPE_DOUBLE: - rb_ary_push(params, rb_float_new(*(double *)x->args[i])); - break; - case TYPE_FLOAT: - rb_ary_push(params, rb_float_new(*(float *)x->args[i])); - break; -#if HAVE_LONG_LONG - case TYPE_LONG_LONG: - rb_ary_push(params, LL2NUM(*(LONG_LONG *)x->args[i])); - break; - case TYPE_ULONG_LONG: - rb_ary_push(params, ULL2NUM(*(unsigned LONG_LONG *)x->args[i])); - break; -#endif - case TYPE_CONST_STRING: - rb_ary_push(params, - rb_str_new_cstr(*((const char **)(x->args[i])))); - break; - case TYPE_BOOL: - if (sizeof(bool) == sizeof(char)) { - rb_ary_push(params, CBOOL2RBBOOL(*(unsigned char *)x->args[i])); - } else if (sizeof(bool) == sizeof(short)) { - rb_ary_push(params, CBOOL2RBBOOL(*(unsigned short *)x->args[i])); - } else if (sizeof(bool) == sizeof(int)) { - rb_ary_push(params, CBOOL2RBBOOL(*(unsigned int *)x->args[i])); - } else if (sizeof(bool) == sizeof(long)) { - rb_ary_push(params, CBOOL2RBBOOL(*(unsigned long *)x->args[i])); - } else { - rb_raise(rb_eNotImpError, "bool isn't supported: %u", - (unsigned int)sizeof(bool)); - } - break; - default: - rb_raise(rb_eRuntimeError, "closure args: %d", type); - } - } - - ret = rb_funcall2(self, rb_intern("call"), argc, RARRAY_CONST_PTR(params)); - RB_GC_GUARD(params); - - type = NUM2INT(ctype); - switch (type) { - case TYPE_VOID: - break; - case TYPE_LONG: - *(long *)x->resp = NUM2LONG(ret); - break; - case TYPE_ULONG: - *(unsigned long *)x->resp = NUM2ULONG(ret); - break; - case TYPE_CHAR: - case TYPE_SHORT: - case TYPE_INT: - *(ffi_sarg *)x->resp = NUM2INT(ret); - break; - case TYPE_UCHAR: - case TYPE_USHORT: - case TYPE_UINT: - *(ffi_arg *)x->resp = NUM2UINT(ret); - break; - case TYPE_VOIDP: - *(void **)x->resp = NUM2PTR(ret); - break; - case TYPE_DOUBLE: - *(double *)x->resp = NUM2DBL(ret); - break; - case TYPE_FLOAT: - *(float *)x->resp = (float)NUM2DBL(ret); - break; -#if HAVE_LONG_LONG - case TYPE_LONG_LONG: - *(LONG_LONG *)x->resp = NUM2LL(ret); - break; - case TYPE_ULONG_LONG: - *(unsigned LONG_LONG *)x->resp = NUM2ULL(ret); - break; -#endif - case TYPE_CONST_STRING: - /* Dangerous. Callback must keep reference of the String. */ - *((const char **)(x->resp)) = StringValueCStr(ret); - break; - case TYPE_BOOL: - if (sizeof(bool) == sizeof(long)) { - *(unsigned long *)x->resp = RB_TEST(ret); - } else { - *(ffi_arg *)x->resp = RB_TEST(ret); - } - break; - default: - rb_raise(rb_eRuntimeError, "closure retval: %d", type); - } - return 0; -} - -static void -callback(ffi_cif *cif, void *resp, void **args, void *ctx) -{ - struct callback_args x; - - x.cif = cif; - x.resp = resp; - x.args = args; - x.ctx = ctx; - - if (ruby_thread_has_gvl_p()) { - (void)with_gvl_callback(&x); - } else { - (void)rb_thread_call_with_gvl(with_gvl_callback, &x); - } -} - -static VALUE -allocate(VALUE klass) -{ - fiddle_closure * closure; - - VALUE i = TypedData_Make_Struct(klass, fiddle_closure, - &closure_data_type, closure); - -#if USE_FFI_CLOSURE_ALLOC - closure->pcl = ffi_closure_alloc(sizeof(ffi_closure), &closure->code); -#else - closure->pcl = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE, - MAP_ANON | MAP_PRIVATE, -1, 0); -#endif - - return i; -} - -static fiddle_closure * -get_raw(VALUE self) -{ - fiddle_closure *closure; - TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, closure); - if (!closure) { - rb_raise(rb_eArgError, "already freed: %+"PRIsVALUE, self); - } - return closure; -} - -typedef struct { - VALUE self; - int argc; - VALUE *argv; -} initialize_data; - -static VALUE -initialize_body(VALUE user_data) -{ - initialize_data *data = (initialize_data *)user_data; - VALUE ret; - VALUE args; - VALUE normalized_args; - VALUE abi; - fiddle_closure * cl; - ffi_cif * cif; - ffi_closure *pcl; - ffi_status result; - int i, argc; - - if (2 == rb_scan_args(data->argc, data->argv, "21", &ret, &args, &abi)) - abi = INT2NUM(FFI_DEFAULT_ABI); - - Check_Type(args, T_ARRAY); - - argc = RARRAY_LENINT(args); - - TypedData_Get_Struct(data->self, fiddle_closure, &closure_data_type, cl); - - cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *)); - - normalized_args = rb_ary_new_capa(argc); - for (i = 0; i < argc; i++) { - VALUE arg = rb_fiddle_type_ensure(RARRAY_AREF(args, i)); - rb_ary_push(normalized_args, arg); - cl->argv[i] = rb_fiddle_int_to_ffi_type(NUM2INT(arg)); - } - cl->argv[argc] = NULL; - - ret = rb_fiddle_type_ensure(ret); - rb_iv_set(data->self, "@ctype", ret); - rb_iv_set(data->self, "@args", normalized_args); - - cif = &cl->cif; - pcl = cl->pcl; - - result = ffi_prep_cif(cif, - NUM2INT(abi), - argc, - rb_fiddle_int_to_ffi_type(NUM2INT(ret)), - cl->argv); - - if (FFI_OK != result) { - rb_raise(rb_eRuntimeError, "error prepping CIF %d", result); - } - -#if USE_FFI_CLOSURE_ALLOC - result = ffi_prep_closure_loc(pcl, cif, callback, - (void *)(data->self), cl->code); -#else - result = ffi_prep_closure(pcl, cif, callback, (void *)(data->self)); - cl->code = (void *)pcl; - i = mprotect(pcl, sizeof(*pcl), PROT_READ | PROT_EXEC); - if (i) { - rb_sys_fail("mprotect"); - } -#endif - - if (FFI_OK != result) { - rb_raise(rb_eRuntimeError, "error prepping closure %d", result); - } - - return data->self; -} - -static VALUE -initialize_rescue(VALUE user_data, VALUE exception) -{ - initialize_data *data = (initialize_data *)user_data; - dealloc(RTYPEDDATA_DATA(data->self)); - RTYPEDDATA_DATA(data->self) = NULL; - rb_exc_raise(exception); - return data->self; -} - -static VALUE -initialize(int argc, VALUE *argv, VALUE self) -{ - initialize_data data; - data.self = self; - data.argc = argc; - data.argv = argv; - return rb_rescue(initialize_body, (VALUE)&data, - initialize_rescue, (VALUE)&data); -} - -static VALUE -to_i(VALUE self) -{ - fiddle_closure *closure = get_raw(self); - return PTR2NUM(closure->code); -} - -static VALUE -closure_free(VALUE self) -{ - fiddle_closure *closure; - TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, closure); - if (closure) { - dealloc(closure); - RTYPEDDATA_DATA(self) = NULL; - } - return RUBY_Qnil; -} - -static VALUE -closure_freed_p(VALUE self) -{ - fiddle_closure *closure; - TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, closure); - return closure ? RUBY_Qfalse : RUBY_Qtrue; -} - -void -Init_fiddle_closure(void) -{ -#if 0 - mFiddle = rb_define_module("Fiddle"); /* let rdoc know about mFiddle */ -#endif - - /* - * Document-class: Fiddle::Closure - * - * == Description - * - * An FFI closure wrapper, for handling callbacks. - * - * == Example - * - * closure = Class.new(Fiddle::Closure) { - * def call - * 10 - * end - * }.new(Fiddle::TYPE_INT, []) - * #=> #<#<Class:0x0000000150d308>:0x0000000150d240> - * func = Fiddle::Function.new(closure, [], Fiddle::TYPE_INT) - * #=> #<Fiddle::Function:0x00000001516e58> - * func.call - * #=> 10 - */ - cFiddleClosure = rb_define_class_under(mFiddle, "Closure", rb_cObject); - - rb_define_alloc_func(cFiddleClosure, allocate); - - /* - * Document-method: new - * - * call-seq: new(ret, args, abi = Fiddle::DEFAULT) - * - * Construct a new Closure object. - * - * * +ret+ is the C type to be returned - * * +args+ is an Array of arguments, passed to the callback function - * * +abi+ is the abi of the closure - * - * If there is an error in preparing the ffi_cif or ffi_prep_closure, - * then a RuntimeError will be raised. - */ - rb_define_method(cFiddleClosure, "initialize", initialize, -1); - - /* - * Document-method: to_i - * - * Returns the memory address for this closure. - */ - rb_define_method(cFiddleClosure, "to_i", to_i, 0); - - /* - * Document-method: free - * - * Free this closure explicitly. You can't use this closure anymore. - * - * If this closure is already freed, this does nothing. - */ - rb_define_method(cFiddleClosure, "free", closure_free, 0); - - /* - * Document-method: freed? - * - * Whether this closure was freed explicitly. - */ - rb_define_method(cFiddleClosure, "freed?", closure_freed_p, 0); -} -/* vim: set noet sw=4 sts=4 */ diff --git a/ext/fiddle/closure.h b/ext/fiddle/closure.h deleted file mode 100644 index d0a8be6180..0000000000 --- a/ext/fiddle/closure.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef FIDDLE_CLOSURE_H -#define FIDDLE_CLOSURE_H - -#include <fiddle.h> - -void Init_fiddle_closure(void); - -#endif diff --git a/ext/fiddle/conversions.c b/ext/fiddle/conversions.c deleted file mode 100644 index 796bf929c3..0000000000 --- a/ext/fiddle/conversions.c +++ /dev/null @@ -1,383 +0,0 @@ -#include <stdbool.h> - -#include <fiddle.h> - -VALUE -rb_fiddle_type_ensure(VALUE type) -{ - VALUE original_type = type; - - if (!RB_SYMBOL_P(type)) { - VALUE type_string = rb_check_string_type(type); - if (!NIL_P(type_string)) { - type = rb_to_symbol(type_string); - } - } - - if (RB_SYMBOL_P(type)) { - ID type_id = rb_sym2id(type); - ID void_id; - ID voidp_id; - ID char_id; - ID short_id; - ID int_id; - ID long_id; -#ifdef TYPE_LONG_LONG - ID long_long_id; -#endif -#ifdef TYPE_INT8_T - ID int8_t_id; -#endif -#ifdef TYPE_INT16_T - ID int16_t_id; -#endif -#ifdef TYPE_INT32_T - ID int32_t_id; -#endif -#ifdef TYPE_INT64_T - ID int64_t_id; -#endif - ID float_id; - ID double_id; - ID variadic_id; - ID const_string_id; - ID size_t_id; - ID ssize_t_id; - ID ptrdiff_t_id; - ID intptr_t_id; - ID uintptr_t_id; - ID bool_id; - RUBY_CONST_ID(void_id, "void"); - RUBY_CONST_ID(voidp_id, "voidp"); - RUBY_CONST_ID(char_id, "char"); - RUBY_CONST_ID(short_id, "short"); - RUBY_CONST_ID(int_id, "int"); - RUBY_CONST_ID(long_id, "long"); -#ifdef TYPE_LONG_LONG - RUBY_CONST_ID(long_long_id, "long_long"); -#endif -#ifdef TYPE_INT8_T - RUBY_CONST_ID(int8_t_id, "int8_t"); -#endif -#ifdef TYPE_INT16_T - RUBY_CONST_ID(int16_t_id, "int16_t"); -#endif -#ifdef TYPE_INT32_T - RUBY_CONST_ID(int32_t_id, "int32_t"); -#endif -#ifdef TYPE_INT64_T - RUBY_CONST_ID(int64_t_id, "int64_t"); -#endif - RUBY_CONST_ID(float_id, "float"); - RUBY_CONST_ID(double_id, "double"); - RUBY_CONST_ID(variadic_id, "variadic"); - RUBY_CONST_ID(const_string_id, "const_string"); - RUBY_CONST_ID(size_t_id, "size_t"); - RUBY_CONST_ID(ssize_t_id, "ssize_t"); - RUBY_CONST_ID(ptrdiff_t_id, "ptrdiff_t"); - RUBY_CONST_ID(intptr_t_id, "intptr_t"); - RUBY_CONST_ID(uintptr_t_id, "uintptr_t"); - RUBY_CONST_ID(bool_id, "bool"); - if (type_id == void_id) { - return INT2NUM(TYPE_VOID); - } - else if (type_id == voidp_id) { - return INT2NUM(TYPE_VOIDP); - } - else if (type_id == char_id) { - return INT2NUM(TYPE_CHAR); - } - else if (type_id == short_id) { - return INT2NUM(TYPE_SHORT); - } - else if (type_id == int_id) { - return INT2NUM(TYPE_INT); - } - else if (type_id == long_id) { - return INT2NUM(TYPE_LONG); - } -#ifdef TYPE_LONG_LONG - else if (type_id == long_long_id) { - return INT2NUM(TYPE_LONG_LONG); - } -#endif -#ifdef TYPE_INT8_T - else if (type_id == int8_t_id) { - return INT2NUM(TYPE_INT8_T); - } -#endif -#ifdef TYPE_INT16_T - else if (type_id == int16_t_id) { - return INT2NUM(TYPE_INT16_T); - } -#endif -#ifdef TYPE_INT32_T - else if (type_id == int32_t_id) { - return INT2NUM(TYPE_INT32_T); - } -#endif -#ifdef TYPE_INT64_T - else if (type_id == int64_t_id) { - return INT2NUM(TYPE_INT64_T); - } -#endif - else if (type_id == float_id) { - return INT2NUM(TYPE_FLOAT); - } - else if (type_id == double_id) { - return INT2NUM(TYPE_DOUBLE); - } - else if (type_id == variadic_id) { - return INT2NUM(TYPE_VARIADIC); - } - else if (type_id == const_string_id) { - return INT2NUM(TYPE_CONST_STRING); - } - else if (type_id == size_t_id) { - return INT2NUM(TYPE_SIZE_T); - } - else if (type_id == ssize_t_id) { - return INT2NUM(TYPE_SSIZE_T); - } - else if (type_id == ptrdiff_t_id) { - return INT2NUM(TYPE_PTRDIFF_T); - } - else if (type_id == intptr_t_id) { - return INT2NUM(TYPE_INTPTR_T); - } - else if (type_id == uintptr_t_id) { - return INT2NUM(TYPE_UINTPTR_T); - } - else if (type_id == bool_id) { - return INT2NUM(TYPE_BOOL); - } - else { - type = original_type; - } - } - - return rb_to_int(type); -} - -ffi_type * -rb_fiddle_int_to_ffi_type(int type) -{ - int signed_p = 1; - - if (type < 0) { - type = -1 * type; - signed_p = 0; - } - -#define rb_ffi_type_of(t) (signed_p ? &ffi_type_s##t : &ffi_type_u##t) - - switch (type) { - case TYPE_VOID: - return &ffi_type_void; - case TYPE_VOIDP: - return &ffi_type_pointer; - case TYPE_CHAR: - return rb_ffi_type_of(char); - case TYPE_SHORT: - return rb_ffi_type_of(short); - case TYPE_INT: - return rb_ffi_type_of(int); - case TYPE_LONG: - return rb_ffi_type_of(long); -#if HAVE_LONG_LONG - case TYPE_LONG_LONG: - return rb_ffi_type_of(long_long); -#endif - case TYPE_FLOAT: - return &ffi_type_float; - case TYPE_DOUBLE: - return &ffi_type_double; - case TYPE_CONST_STRING: - return &ffi_type_pointer; - case TYPE_BOOL: - signed_p = 0; - if (sizeof(bool) == sizeof(char)) { - return rb_ffi_type_of(char); - } else if (sizeof(bool) == sizeof(short)) { - return rb_ffi_type_of(short); - } else if (sizeof(bool) == sizeof(int)) { - return rb_ffi_type_of(int); - } else if (sizeof(bool) == sizeof(long)) { - return rb_ffi_type_of(long); - } else { - rb_raise(rb_eNotImpError, "bool isn't supported: %u", - (unsigned int)sizeof(bool)); - } - default: - rb_raise(rb_eRuntimeError, "unknown type %d", type); - } - return &ffi_type_pointer; -} - -ffi_type * -int_to_ffi_type(int type) -{ - return rb_fiddle_int_to_ffi_type(type); -} - -void -rb_fiddle_value_to_generic(int type, VALUE *src, fiddle_generic *dst) -{ - switch (type) { - case TYPE_VOID: - break; - case TYPE_VOIDP: - dst->pointer = NUM2PTR(rb_Integer(*src)); - break; - case TYPE_CHAR: - if (RB_TYPE_P(*src, RUBY_T_STRING) && RSTRING_LEN(*src) == 1) { - dst->schar = RSTRING_PTR(*src)[0]; - } else { - dst->schar = (signed char)NUM2INT(*src); - } - break; - case TYPE_UCHAR: - dst->uchar = (unsigned char)NUM2UINT(*src); - break; - case TYPE_SHORT: - dst->sshort = (unsigned short)NUM2INT(*src); - break; - case TYPE_USHORT: - dst->sshort = (signed short)NUM2UINT(*src); - break; - case TYPE_INT: - dst->sint = NUM2INT(*src); - break; - case TYPE_UINT: - dst->uint = NUM2UINT(*src); - break; - case TYPE_LONG: - dst->slong = NUM2LONG(*src); - break; - case TYPE_ULONG: - dst->ulong = NUM2ULONG(*src); - break; -#if HAVE_LONG_LONG - case TYPE_LONG_LONG: - dst->slong_long = NUM2LL(*src); - break; - case TYPE_ULONG_LONG: - dst->ulong_long = NUM2ULL(*src); - break; -#endif - case TYPE_FLOAT: - dst->ffloat = (float)NUM2DBL(*src); - break; - case TYPE_DOUBLE: - dst->ddouble = NUM2DBL(*src); - break; - case TYPE_CONST_STRING: - if (NIL_P(*src)) { - dst->pointer = NULL; - } - else { - dst->pointer = rb_string_value_cstr(src); - } - break; - case TYPE_BOOL: - if (sizeof(bool) == sizeof(char)) { - dst->uchar = RB_TEST(*src); - } else if (sizeof(bool) == sizeof(short)) { - dst->ushort = RB_TEST(*src); - } else if (sizeof(bool) == sizeof(int)) { - dst->uint = RB_TEST(*src); - } else if (sizeof(bool) == sizeof(long)) { - dst->ulong = RB_TEST(*src); - } else { - rb_raise(rb_eNotImpError, "bool isn't supported: %u", - (unsigned int)sizeof(bool)); - } - break; - default: - rb_raise(rb_eRuntimeError, "unknown type %d", type); - break; - } -} - -void -value_to_generic(int type, VALUE src, fiddle_generic *dst) -{ - /* src isn't safe from GC when type is TYPE_CONST_STRING and src - * isn't String. */ - rb_fiddle_value_to_generic(type, &src, dst); -} - -VALUE -rb_fiddle_generic_to_value(VALUE rettype, fiddle_generic retval) -{ - int type = NUM2INT(rettype); - VALUE cPointer; - - cPointer = rb_const_get(mFiddle, rb_intern("Pointer")); - - switch (type) { - case TYPE_VOID: - return Qnil; - case TYPE_VOIDP: - return rb_funcall(cPointer, rb_intern("[]"), 1, - PTR2NUM((void *)retval.pointer)); - case TYPE_CHAR: - return INT2NUM((signed char)retval.fffi_sarg); - case TYPE_UCHAR: - return INT2NUM((unsigned char)retval.fffi_arg); - case TYPE_SHORT: - return INT2NUM((signed short)retval.fffi_sarg); - case TYPE_USHORT: - return INT2NUM((unsigned short)retval.fffi_arg); - case TYPE_INT: - return INT2NUM((signed int)retval.fffi_sarg); - case TYPE_UINT: - return UINT2NUM((unsigned int)retval.fffi_arg); - case TYPE_LONG: - return LONG2NUM(retval.slong); - case TYPE_ULONG: - return ULONG2NUM(retval.ulong); -#if HAVE_LONG_LONG - case TYPE_LONG_LONG: - return LL2NUM(retval.slong_long); - case TYPE_ULONG_LONG: - return ULL2NUM(retval.ulong_long); -#endif - case TYPE_FLOAT: - return rb_float_new(retval.ffloat); - case TYPE_DOUBLE: - return rb_float_new(retval.ddouble); - case TYPE_CONST_STRING: - if (retval.pointer) { - return rb_str_new_cstr(retval.pointer); - } - else { - return Qnil; - } - case TYPE_BOOL: - if (sizeof(bool) == sizeof(char)) { - return CBOOL2RBBOOL((unsigned char)retval.fffi_arg); - } else if (sizeof(bool) == sizeof(short)) { - return CBOOL2RBBOOL((unsigned short)retval.fffi_arg); - } else if (sizeof(bool) == sizeof(int)) { - return CBOOL2RBBOOL((unsigned int)retval.fffi_arg); - } else if (sizeof(bool) == sizeof(long)) { - return CBOOL2RBBOOL(retval.ulong); - } else { - rb_raise(rb_eNotImpError, "bool isn't supported: %u", - (unsigned int)sizeof(bool)); - } - default: - rb_raise(rb_eRuntimeError, "unknown type %d", type); - } - - UNREACHABLE; -} - -VALUE -generic_to_value(VALUE rettype, fiddle_generic retval) -{ - return rb_fiddle_generic_to_value(rettype, retval); -} - -/* vim: set noet sw=4 sts=4 */ diff --git a/ext/fiddle/conversions.h b/ext/fiddle/conversions.h deleted file mode 100644 index 7a1e928d56..0000000000 --- a/ext/fiddle/conversions.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef FIDDLE_CONVERSIONS_H -#define FIDDLE_CONVERSIONS_H - -#include <fiddle.h> - -typedef union -{ - ffi_arg fffi_arg; /* rvalue smaller than unsigned long */ - ffi_sarg fffi_sarg; /* rvalue smaller than signed long */ - unsigned char uchar; /* ffi_type_uchar */ - signed char schar; /* ffi_type_schar */ - unsigned short ushort; /* ffi_type_sshort */ - signed short sshort; /* ffi_type_ushort */ - unsigned int uint; /* ffi_type_uint */ - signed int sint; /* ffi_type_sint */ - unsigned long ulong; /* ffi_type_ulong */ - signed long slong; /* ffi_type_slong */ - float ffloat; /* ffi_type_float */ - double ddouble; /* ffi_type_double */ -#if HAVE_LONG_LONG - unsigned LONG_LONG ulong_long; /* ffi_type_ulong_long */ - signed LONG_LONG slong_long; /* ffi_type_ulong_long */ -#endif - void * pointer; /* ffi_type_pointer */ -} fiddle_generic; - -VALUE rb_fiddle_type_ensure(VALUE type); -ffi_type * rb_fiddle_int_to_ffi_type(int type); -void rb_fiddle_value_to_generic(int type, VALUE *src, fiddle_generic *dst); -VALUE rb_fiddle_generic_to_value(VALUE rettype, fiddle_generic retval); - -/* Deprecated. Use rb_fiddle_*() version. */ -ffi_type * int_to_ffi_type(int type); -void value_to_generic(int type, VALUE src, fiddle_generic *dst); -VALUE generic_to_value(VALUE rettype, fiddle_generic retval); - -#define VALUE2GENERIC(_type, _src, _dst) \ - rb_fiddle_value_to_generic((_type), &(_src), (_dst)) -#define INT2FFI_TYPE(_type) \ - rb_fiddle_int_to_ffi_type(_type) -#define GENERIC2VALUE(_type, _retval) \ - rb_fiddle_generic_to_value((_type), (_retval)) - -#if SIZEOF_VOIDP == SIZEOF_LONG -# define PTR2NUM(x) (LONG2NUM((long)(x))) -# define NUM2PTR(x) ((void*)(NUM2ULONG(x))) -#else -/* # error --->> Ruby/DL2 requires sizeof(void*) == sizeof(long) to be compiled. <<--- */ -# define PTR2NUM(x) (LL2NUM((LONG_LONG)(x))) -# define NUM2PTR(x) ((void*)(NUM2ULL(x))) -#endif - -#define CBOOL2RBBOOL(cbool) ((cbool) ? RUBY_Qtrue : RUBY_Qfalse) - -#endif diff --git a/ext/fiddle/depend b/ext/fiddle/depend deleted file mode 100644 index 561785275e..0000000000 --- a/ext/fiddle/depend +++ /dev/null @@ -1,1388 +0,0 @@ -PWD = - -CONFIGURE_LIBFFI = \ - $(LIBFFI_CONFIGURE) --disable-shared \ - --host=$(LIBFFI_ARCH) --enable-builddir=$(arch) \ - CC="$(CC)" CFLAGS="$(LIBFFI_CFLAGS)" \ - LD="$(LD)" LDFLAGS="$(LIBFFI_LDFLAGS)" - -$(STATIC_LIB) $(RUBYARCHDIR)/$(DLLIB) $(DLLIB): $(LIBFFI_A) - -$(OBJS): $(FFI_H) - -.PHONY: .FORCE hdr - -.FORCE: - -hdr: $(FFI_H) - -configure-libffi build-libffi: .FORCE -configure-libffi \ -$(LIBFFI_DIR)/include/ffi.h \ -$(LIBFFI_DIR)/include/ffitarget.h \ -$(LIBFFI_DIR)/fficonfig.h \ -$(LIBFFI_DIR)/Makefile: - $(Q) $(MAKEDIRS) $(LIBFFI_DIR) - $(Q) $(CONFIGURE_LIBFFI) - -build-libffi $(LIBFFI_A): - $(Q) $(SUBMAKE_PRE) $(MAKE) $(SUBMAKE_ARG) - -clean-none: -clean-libffi: - $(Q) $(SUBMAKE_PRE) $(MAKE) $(SUBMAKE_ARG) clean - -distclean-none: -distclean-libffi: - $(Q) $(SUBMAKE_PRE) $(MAKE) $(SUBMAKE_ARG) distclean - $(Q) $(RM) $(LIBFFI_DIR)/local.exp - $(Q) $(RUBY) -rfileutils -e "FileUtils.rmdir(Dir.glob(ARGV[0]+'/**/{,.*/}'), :parents=>true)" $(LIBFFI_DIR) - -realclean-none: -realclean-libffi: - $(Q) $(RMALL) $(LIBFFI_DIR) - -.PHONY: clean-libffi distclean-libffi realclean-libffi -.PHONY: clean-none distclean-none realclean-none - -clean: clean-$(LIBFFI_CLEAN) -distclean: distclean-$(LIBFFI_CLEAN) -realclean: realclean-$(LIBFFI_CLEAN) - -.PHONY: configure configure-libffi - -# AUTOGENERATED DEPENDENCIES START -closure.o: $(RUBY_EXTCONF_H) -closure.o: $(arch_hdrdir)/ruby/config.h -closure.o: $(hdrdir)/ruby.h -closure.o: $(hdrdir)/ruby/assert.h -closure.o: $(hdrdir)/ruby/backward.h -closure.o: $(hdrdir)/ruby/backward/2/assume.h -closure.o: $(hdrdir)/ruby/backward/2/attributes.h -closure.o: $(hdrdir)/ruby/backward/2/bool.h -closure.o: $(hdrdir)/ruby/backward/2/inttypes.h -closure.o: $(hdrdir)/ruby/backward/2/limits.h -closure.o: $(hdrdir)/ruby/backward/2/long_long.h -closure.o: $(hdrdir)/ruby/backward/2/stdalign.h -closure.o: $(hdrdir)/ruby/backward/2/stdarg.h -closure.o: $(hdrdir)/ruby/defines.h -closure.o: $(hdrdir)/ruby/intern.h -closure.o: $(hdrdir)/ruby/internal/abi.h -closure.o: $(hdrdir)/ruby/internal/anyargs.h -closure.o: $(hdrdir)/ruby/internal/arithmetic.h -closure.o: $(hdrdir)/ruby/internal/arithmetic/char.h -closure.o: $(hdrdir)/ruby/internal/arithmetic/double.h -closure.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h -closure.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h -closure.o: $(hdrdir)/ruby/internal/arithmetic/int.h -closure.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h -closure.o: $(hdrdir)/ruby/internal/arithmetic/long.h -closure.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h -closure.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h -closure.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h -closure.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h -closure.o: $(hdrdir)/ruby/internal/arithmetic/short.h -closure.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h -closure.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h -closure.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h -closure.o: $(hdrdir)/ruby/internal/assume.h -closure.o: $(hdrdir)/ruby/internal/attr/alloc_size.h -closure.o: $(hdrdir)/ruby/internal/attr/artificial.h -closure.o: $(hdrdir)/ruby/internal/attr/cold.h -closure.o: $(hdrdir)/ruby/internal/attr/const.h -closure.o: $(hdrdir)/ruby/internal/attr/constexpr.h -closure.o: $(hdrdir)/ruby/internal/attr/deprecated.h -closure.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h -closure.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h -closure.o: $(hdrdir)/ruby/internal/attr/error.h -closure.o: $(hdrdir)/ruby/internal/attr/flag_enum.h -closure.o: $(hdrdir)/ruby/internal/attr/forceinline.h -closure.o: $(hdrdir)/ruby/internal/attr/format.h -closure.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h -closure.o: $(hdrdir)/ruby/internal/attr/noalias.h -closure.o: $(hdrdir)/ruby/internal/attr/nodiscard.h -closure.o: $(hdrdir)/ruby/internal/attr/noexcept.h -closure.o: $(hdrdir)/ruby/internal/attr/noinline.h -closure.o: $(hdrdir)/ruby/internal/attr/nonnull.h -closure.o: $(hdrdir)/ruby/internal/attr/noreturn.h -closure.o: $(hdrdir)/ruby/internal/attr/packed_struct.h -closure.o: $(hdrdir)/ruby/internal/attr/pure.h -closure.o: $(hdrdir)/ruby/internal/attr/restrict.h -closure.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h -closure.o: $(hdrdir)/ruby/internal/attr/warning.h -closure.o: $(hdrdir)/ruby/internal/attr/weakref.h -closure.o: $(hdrdir)/ruby/internal/cast.h -closure.o: $(hdrdir)/ruby/internal/compiler_is.h -closure.o: $(hdrdir)/ruby/internal/compiler_is/apple.h -closure.o: $(hdrdir)/ruby/internal/compiler_is/clang.h -closure.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h -closure.o: $(hdrdir)/ruby/internal/compiler_is/intel.h -closure.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h -closure.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h -closure.o: $(hdrdir)/ruby/internal/compiler_since.h -closure.o: $(hdrdir)/ruby/internal/config.h -closure.o: $(hdrdir)/ruby/internal/constant_p.h -closure.o: $(hdrdir)/ruby/internal/core.h -closure.o: $(hdrdir)/ruby/internal/core/rarray.h -closure.o: $(hdrdir)/ruby/internal/core/rbasic.h -closure.o: $(hdrdir)/ruby/internal/core/rbignum.h -closure.o: $(hdrdir)/ruby/internal/core/rclass.h -closure.o: $(hdrdir)/ruby/internal/core/rdata.h -closure.o: $(hdrdir)/ruby/internal/core/rfile.h -closure.o: $(hdrdir)/ruby/internal/core/rhash.h -closure.o: $(hdrdir)/ruby/internal/core/robject.h -closure.o: $(hdrdir)/ruby/internal/core/rregexp.h -closure.o: $(hdrdir)/ruby/internal/core/rstring.h -closure.o: $(hdrdir)/ruby/internal/core/rstruct.h -closure.o: $(hdrdir)/ruby/internal/core/rtypeddata.h -closure.o: $(hdrdir)/ruby/internal/ctype.h -closure.o: $(hdrdir)/ruby/internal/dllexport.h -closure.o: $(hdrdir)/ruby/internal/dosish.h -closure.o: $(hdrdir)/ruby/internal/error.h -closure.o: $(hdrdir)/ruby/internal/eval.h -closure.o: $(hdrdir)/ruby/internal/event.h -closure.o: $(hdrdir)/ruby/internal/fl_type.h -closure.o: $(hdrdir)/ruby/internal/gc.h -closure.o: $(hdrdir)/ruby/internal/glob.h -closure.o: $(hdrdir)/ruby/internal/globals.h -closure.o: $(hdrdir)/ruby/internal/has/attribute.h -closure.o: $(hdrdir)/ruby/internal/has/builtin.h -closure.o: $(hdrdir)/ruby/internal/has/c_attribute.h -closure.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h -closure.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h -closure.o: $(hdrdir)/ruby/internal/has/extension.h -closure.o: $(hdrdir)/ruby/internal/has/feature.h -closure.o: $(hdrdir)/ruby/internal/has/warning.h -closure.o: $(hdrdir)/ruby/internal/intern/array.h -closure.o: $(hdrdir)/ruby/internal/intern/bignum.h -closure.o: $(hdrdir)/ruby/internal/intern/class.h -closure.o: $(hdrdir)/ruby/internal/intern/compar.h -closure.o: $(hdrdir)/ruby/internal/intern/complex.h -closure.o: $(hdrdir)/ruby/internal/intern/cont.h -closure.o: $(hdrdir)/ruby/internal/intern/dir.h -closure.o: $(hdrdir)/ruby/internal/intern/enum.h -closure.o: $(hdrdir)/ruby/internal/intern/enumerator.h -closure.o: $(hdrdir)/ruby/internal/intern/error.h -closure.o: $(hdrdir)/ruby/internal/intern/eval.h -closure.o: $(hdrdir)/ruby/internal/intern/file.h -closure.o: $(hdrdir)/ruby/internal/intern/hash.h -closure.o: $(hdrdir)/ruby/internal/intern/io.h -closure.o: $(hdrdir)/ruby/internal/intern/load.h -closure.o: $(hdrdir)/ruby/internal/intern/marshal.h -closure.o: $(hdrdir)/ruby/internal/intern/numeric.h -closure.o: $(hdrdir)/ruby/internal/intern/object.h -closure.o: $(hdrdir)/ruby/internal/intern/parse.h -closure.o: $(hdrdir)/ruby/internal/intern/proc.h -closure.o: $(hdrdir)/ruby/internal/intern/process.h -closure.o: $(hdrdir)/ruby/internal/intern/random.h -closure.o: $(hdrdir)/ruby/internal/intern/range.h -closure.o: $(hdrdir)/ruby/internal/intern/rational.h -closure.o: $(hdrdir)/ruby/internal/intern/re.h -closure.o: $(hdrdir)/ruby/internal/intern/ruby.h -closure.o: $(hdrdir)/ruby/internal/intern/select.h -closure.o: $(hdrdir)/ruby/internal/intern/select/largesize.h -closure.o: $(hdrdir)/ruby/internal/intern/signal.h -closure.o: $(hdrdir)/ruby/internal/intern/sprintf.h -closure.o: $(hdrdir)/ruby/internal/intern/string.h -closure.o: $(hdrdir)/ruby/internal/intern/struct.h -closure.o: $(hdrdir)/ruby/internal/intern/thread.h -closure.o: $(hdrdir)/ruby/internal/intern/time.h -closure.o: $(hdrdir)/ruby/internal/intern/variable.h -closure.o: $(hdrdir)/ruby/internal/intern/vm.h -closure.o: $(hdrdir)/ruby/internal/interpreter.h -closure.o: $(hdrdir)/ruby/internal/iterator.h -closure.o: $(hdrdir)/ruby/internal/memory.h -closure.o: $(hdrdir)/ruby/internal/method.h -closure.o: $(hdrdir)/ruby/internal/module.h -closure.o: $(hdrdir)/ruby/internal/newobj.h -closure.o: $(hdrdir)/ruby/internal/scan_args.h -closure.o: $(hdrdir)/ruby/internal/special_consts.h -closure.o: $(hdrdir)/ruby/internal/static_assert.h -closure.o: $(hdrdir)/ruby/internal/stdalign.h -closure.o: $(hdrdir)/ruby/internal/stdbool.h -closure.o: $(hdrdir)/ruby/internal/symbol.h -closure.o: $(hdrdir)/ruby/internal/value.h -closure.o: $(hdrdir)/ruby/internal/value_type.h -closure.o: $(hdrdir)/ruby/internal/variable.h -closure.o: $(hdrdir)/ruby/internal/warning_push.h -closure.o: $(hdrdir)/ruby/internal/xmalloc.h -closure.o: $(hdrdir)/ruby/missing.h -closure.o: $(hdrdir)/ruby/ruby.h -closure.o: $(hdrdir)/ruby/st.h -closure.o: $(hdrdir)/ruby/subst.h -closure.o: $(hdrdir)/ruby/thread.h -closure.o: closure.c -closure.o: closure.h -closure.o: conversions.h -closure.o: fiddle.h -closure.o: function.h -conversions.o: $(RUBY_EXTCONF_H) -conversions.o: $(arch_hdrdir)/ruby/config.h -conversions.o: $(hdrdir)/ruby.h -conversions.o: $(hdrdir)/ruby/assert.h -conversions.o: $(hdrdir)/ruby/backward.h -conversions.o: $(hdrdir)/ruby/backward/2/assume.h -conversions.o: $(hdrdir)/ruby/backward/2/attributes.h -conversions.o: $(hdrdir)/ruby/backward/2/bool.h -conversions.o: $(hdrdir)/ruby/backward/2/inttypes.h -conversions.o: $(hdrdir)/ruby/backward/2/limits.h -conversions.o: $(hdrdir)/ruby/backward/2/long_long.h -conversions.o: $(hdrdir)/ruby/backward/2/stdalign.h -conversions.o: $(hdrdir)/ruby/backward/2/stdarg.h -conversions.o: $(hdrdir)/ruby/defines.h -conversions.o: $(hdrdir)/ruby/intern.h -conversions.o: $(hdrdir)/ruby/internal/abi.h -conversions.o: $(hdrdir)/ruby/internal/anyargs.h -conversions.o: $(hdrdir)/ruby/internal/arithmetic.h -conversions.o: $(hdrdir)/ruby/internal/arithmetic/char.h -conversions.o: $(hdrdir)/ruby/internal/arithmetic/double.h -conversions.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h -conversions.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h -conversions.o: $(hdrdir)/ruby/internal/arithmetic/int.h -conversions.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h -conversions.o: $(hdrdir)/ruby/internal/arithmetic/long.h -conversions.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h -conversions.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h -conversions.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h -conversions.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h -conversions.o: $(hdrdir)/ruby/internal/arithmetic/short.h -conversions.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h -conversions.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h -conversions.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h -conversions.o: $(hdrdir)/ruby/internal/assume.h -conversions.o: $(hdrdir)/ruby/internal/attr/alloc_size.h -conversions.o: $(hdrdir)/ruby/internal/attr/artificial.h -conversions.o: $(hdrdir)/ruby/internal/attr/cold.h -conversions.o: $(hdrdir)/ruby/internal/attr/const.h -conversions.o: $(hdrdir)/ruby/internal/attr/constexpr.h -conversions.o: $(hdrdir)/ruby/internal/attr/deprecated.h -conversions.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h -conversions.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h -conversions.o: $(hdrdir)/ruby/internal/attr/error.h -conversions.o: $(hdrdir)/ruby/internal/attr/flag_enum.h -conversions.o: $(hdrdir)/ruby/internal/attr/forceinline.h -conversions.o: $(hdrdir)/ruby/internal/attr/format.h -conversions.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h -conversions.o: $(hdrdir)/ruby/internal/attr/noalias.h -conversions.o: $(hdrdir)/ruby/internal/attr/nodiscard.h -conversions.o: $(hdrdir)/ruby/internal/attr/noexcept.h -conversions.o: $(hdrdir)/ruby/internal/attr/noinline.h -conversions.o: $(hdrdir)/ruby/internal/attr/nonnull.h -conversions.o: $(hdrdir)/ruby/internal/attr/noreturn.h -conversions.o: $(hdrdir)/ruby/internal/attr/packed_struct.h -conversions.o: $(hdrdir)/ruby/internal/attr/pure.h -conversions.o: $(hdrdir)/ruby/internal/attr/restrict.h -conversions.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h -conversions.o: $(hdrdir)/ruby/internal/attr/warning.h -conversions.o: $(hdrdir)/ruby/internal/attr/weakref.h -conversions.o: $(hdrdir)/ruby/internal/cast.h -conversions.o: $(hdrdir)/ruby/internal/compiler_is.h -conversions.o: $(hdrdir)/ruby/internal/compiler_is/apple.h -conversions.o: $(hdrdir)/ruby/internal/compiler_is/clang.h -conversions.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h -conversions.o: $(hdrdir)/ruby/internal/compiler_is/intel.h -conversions.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h -conversions.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h -conversions.o: $(hdrdir)/ruby/internal/compiler_since.h -conversions.o: $(hdrdir)/ruby/internal/config.h -conversions.o: $(hdrdir)/ruby/internal/constant_p.h -conversions.o: $(hdrdir)/ruby/internal/core.h -conversions.o: $(hdrdir)/ruby/internal/core/rarray.h -conversions.o: $(hdrdir)/ruby/internal/core/rbasic.h -conversions.o: $(hdrdir)/ruby/internal/core/rbignum.h -conversions.o: $(hdrdir)/ruby/internal/core/rclass.h -conversions.o: $(hdrdir)/ruby/internal/core/rdata.h -conversions.o: $(hdrdir)/ruby/internal/core/rfile.h -conversions.o: $(hdrdir)/ruby/internal/core/rhash.h -conversions.o: $(hdrdir)/ruby/internal/core/robject.h -conversions.o: $(hdrdir)/ruby/internal/core/rregexp.h -conversions.o: $(hdrdir)/ruby/internal/core/rstring.h -conversions.o: $(hdrdir)/ruby/internal/core/rstruct.h -conversions.o: $(hdrdir)/ruby/internal/core/rtypeddata.h -conversions.o: $(hdrdir)/ruby/internal/ctype.h -conversions.o: $(hdrdir)/ruby/internal/dllexport.h -conversions.o: $(hdrdir)/ruby/internal/dosish.h -conversions.o: $(hdrdir)/ruby/internal/error.h -conversions.o: $(hdrdir)/ruby/internal/eval.h -conversions.o: $(hdrdir)/ruby/internal/event.h -conversions.o: $(hdrdir)/ruby/internal/fl_type.h -conversions.o: $(hdrdir)/ruby/internal/gc.h -conversions.o: $(hdrdir)/ruby/internal/glob.h -conversions.o: $(hdrdir)/ruby/internal/globals.h -conversions.o: $(hdrdir)/ruby/internal/has/attribute.h -conversions.o: $(hdrdir)/ruby/internal/has/builtin.h -conversions.o: $(hdrdir)/ruby/internal/has/c_attribute.h -conversions.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h -conversions.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h -conversions.o: $(hdrdir)/ruby/internal/has/extension.h -conversions.o: $(hdrdir)/ruby/internal/has/feature.h -conversions.o: $(hdrdir)/ruby/internal/has/warning.h -conversions.o: $(hdrdir)/ruby/internal/intern/array.h -conversions.o: $(hdrdir)/ruby/internal/intern/bignum.h -conversions.o: $(hdrdir)/ruby/internal/intern/class.h -conversions.o: $(hdrdir)/ruby/internal/intern/compar.h -conversions.o: $(hdrdir)/ruby/internal/intern/complex.h -conversions.o: $(hdrdir)/ruby/internal/intern/cont.h -conversions.o: $(hdrdir)/ruby/internal/intern/dir.h -conversions.o: $(hdrdir)/ruby/internal/intern/enum.h -conversions.o: $(hdrdir)/ruby/internal/intern/enumerator.h -conversions.o: $(hdrdir)/ruby/internal/intern/error.h -conversions.o: $(hdrdir)/ruby/internal/intern/eval.h -conversions.o: $(hdrdir)/ruby/internal/intern/file.h -conversions.o: $(hdrdir)/ruby/internal/intern/hash.h -conversions.o: $(hdrdir)/ruby/internal/intern/io.h -conversions.o: $(hdrdir)/ruby/internal/intern/load.h -conversions.o: $(hdrdir)/ruby/internal/intern/marshal.h -conversions.o: $(hdrdir)/ruby/internal/intern/numeric.h -conversions.o: $(hdrdir)/ruby/internal/intern/object.h -conversions.o: $(hdrdir)/ruby/internal/intern/parse.h -conversions.o: $(hdrdir)/ruby/internal/intern/proc.h -conversions.o: $(hdrdir)/ruby/internal/intern/process.h -conversions.o: $(hdrdir)/ruby/internal/intern/random.h -conversions.o: $(hdrdir)/ruby/internal/intern/range.h -conversions.o: $(hdrdir)/ruby/internal/intern/rational.h -conversions.o: $(hdrdir)/ruby/internal/intern/re.h -conversions.o: $(hdrdir)/ruby/internal/intern/ruby.h -conversions.o: $(hdrdir)/ruby/internal/intern/select.h -conversions.o: $(hdrdir)/ruby/internal/intern/select/largesize.h -conversions.o: $(hdrdir)/ruby/internal/intern/signal.h -conversions.o: $(hdrdir)/ruby/internal/intern/sprintf.h -conversions.o: $(hdrdir)/ruby/internal/intern/string.h -conversions.o: $(hdrdir)/ruby/internal/intern/struct.h -conversions.o: $(hdrdir)/ruby/internal/intern/thread.h -conversions.o: $(hdrdir)/ruby/internal/intern/time.h -conversions.o: $(hdrdir)/ruby/internal/intern/variable.h -conversions.o: $(hdrdir)/ruby/internal/intern/vm.h -conversions.o: $(hdrdir)/ruby/internal/interpreter.h -conversions.o: $(hdrdir)/ruby/internal/iterator.h -conversions.o: $(hdrdir)/ruby/internal/memory.h -conversions.o: $(hdrdir)/ruby/internal/method.h -conversions.o: $(hdrdir)/ruby/internal/module.h -conversions.o: $(hdrdir)/ruby/internal/newobj.h -conversions.o: $(hdrdir)/ruby/internal/scan_args.h -conversions.o: $(hdrdir)/ruby/internal/special_consts.h -conversions.o: $(hdrdir)/ruby/internal/static_assert.h -conversions.o: $(hdrdir)/ruby/internal/stdalign.h -conversions.o: $(hdrdir)/ruby/internal/stdbool.h -conversions.o: $(hdrdir)/ruby/internal/symbol.h -conversions.o: $(hdrdir)/ruby/internal/value.h -conversions.o: $(hdrdir)/ruby/internal/value_type.h -conversions.o: $(hdrdir)/ruby/internal/variable.h -conversions.o: $(hdrdir)/ruby/internal/warning_push.h -conversions.o: $(hdrdir)/ruby/internal/xmalloc.h -conversions.o: $(hdrdir)/ruby/missing.h -conversions.o: $(hdrdir)/ruby/ruby.h -conversions.o: $(hdrdir)/ruby/st.h -conversions.o: $(hdrdir)/ruby/subst.h -conversions.o: closure.h -conversions.o: conversions.c -conversions.o: conversions.h -conversions.o: fiddle.h -conversions.o: function.h -fiddle.o: $(RUBY_EXTCONF_H) -fiddle.o: $(arch_hdrdir)/ruby/config.h -fiddle.o: $(hdrdir)/ruby.h -fiddle.o: $(hdrdir)/ruby/assert.h -fiddle.o: $(hdrdir)/ruby/backward.h -fiddle.o: $(hdrdir)/ruby/backward/2/assume.h -fiddle.o: $(hdrdir)/ruby/backward/2/attributes.h -fiddle.o: $(hdrdir)/ruby/backward/2/bool.h -fiddle.o: $(hdrdir)/ruby/backward/2/inttypes.h -fiddle.o: $(hdrdir)/ruby/backward/2/limits.h -fiddle.o: $(hdrdir)/ruby/backward/2/long_long.h -fiddle.o: $(hdrdir)/ruby/backward/2/stdalign.h -fiddle.o: $(hdrdir)/ruby/backward/2/stdarg.h -fiddle.o: $(hdrdir)/ruby/defines.h -fiddle.o: $(hdrdir)/ruby/intern.h -fiddle.o: $(hdrdir)/ruby/internal/abi.h -fiddle.o: $(hdrdir)/ruby/internal/anyargs.h -fiddle.o: $(hdrdir)/ruby/internal/arithmetic.h -fiddle.o: $(hdrdir)/ruby/internal/arithmetic/char.h -fiddle.o: $(hdrdir)/ruby/internal/arithmetic/double.h -fiddle.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h -fiddle.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h -fiddle.o: $(hdrdir)/ruby/internal/arithmetic/int.h -fiddle.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h -fiddle.o: $(hdrdir)/ruby/internal/arithmetic/long.h -fiddle.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h -fiddle.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h -fiddle.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h -fiddle.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h -fiddle.o: $(hdrdir)/ruby/internal/arithmetic/short.h -fiddle.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h -fiddle.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h -fiddle.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h -fiddle.o: $(hdrdir)/ruby/internal/assume.h -fiddle.o: $(hdrdir)/ruby/internal/attr/alloc_size.h -fiddle.o: $(hdrdir)/ruby/internal/attr/artificial.h -fiddle.o: $(hdrdir)/ruby/internal/attr/cold.h -fiddle.o: $(hdrdir)/ruby/internal/attr/const.h -fiddle.o: $(hdrdir)/ruby/internal/attr/constexpr.h -fiddle.o: $(hdrdir)/ruby/internal/attr/deprecated.h -fiddle.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h -fiddle.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h -fiddle.o: $(hdrdir)/ruby/internal/attr/error.h -fiddle.o: $(hdrdir)/ruby/internal/attr/flag_enum.h -fiddle.o: $(hdrdir)/ruby/internal/attr/forceinline.h -fiddle.o: $(hdrdir)/ruby/internal/attr/format.h -fiddle.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h -fiddle.o: $(hdrdir)/ruby/internal/attr/noalias.h -fiddle.o: $(hdrdir)/ruby/internal/attr/nodiscard.h -fiddle.o: $(hdrdir)/ruby/internal/attr/noexcept.h -fiddle.o: $(hdrdir)/ruby/internal/attr/noinline.h -fiddle.o: $(hdrdir)/ruby/internal/attr/nonnull.h -fiddle.o: $(hdrdir)/ruby/internal/attr/noreturn.h -fiddle.o: $(hdrdir)/ruby/internal/attr/packed_struct.h -fiddle.o: $(hdrdir)/ruby/internal/attr/pure.h -fiddle.o: $(hdrdir)/ruby/internal/attr/restrict.h -fiddle.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h -fiddle.o: $(hdrdir)/ruby/internal/attr/warning.h -fiddle.o: $(hdrdir)/ruby/internal/attr/weakref.h -fiddle.o: $(hdrdir)/ruby/internal/cast.h -fiddle.o: $(hdrdir)/ruby/internal/compiler_is.h -fiddle.o: $(hdrdir)/ruby/internal/compiler_is/apple.h -fiddle.o: $(hdrdir)/ruby/internal/compiler_is/clang.h -fiddle.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h -fiddle.o: $(hdrdir)/ruby/internal/compiler_is/intel.h -fiddle.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h -fiddle.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h -fiddle.o: $(hdrdir)/ruby/internal/compiler_since.h -fiddle.o: $(hdrdir)/ruby/internal/config.h -fiddle.o: $(hdrdir)/ruby/internal/constant_p.h -fiddle.o: $(hdrdir)/ruby/internal/core.h -fiddle.o: $(hdrdir)/ruby/internal/core/rarray.h -fiddle.o: $(hdrdir)/ruby/internal/core/rbasic.h -fiddle.o: $(hdrdir)/ruby/internal/core/rbignum.h -fiddle.o: $(hdrdir)/ruby/internal/core/rclass.h -fiddle.o: $(hdrdir)/ruby/internal/core/rdata.h -fiddle.o: $(hdrdir)/ruby/internal/core/rfile.h -fiddle.o: $(hdrdir)/ruby/internal/core/rhash.h -fiddle.o: $(hdrdir)/ruby/internal/core/robject.h -fiddle.o: $(hdrdir)/ruby/internal/core/rregexp.h -fiddle.o: $(hdrdir)/ruby/internal/core/rstring.h -fiddle.o: $(hdrdir)/ruby/internal/core/rstruct.h -fiddle.o: $(hdrdir)/ruby/internal/core/rtypeddata.h -fiddle.o: $(hdrdir)/ruby/internal/ctype.h -fiddle.o: $(hdrdir)/ruby/internal/dllexport.h -fiddle.o: $(hdrdir)/ruby/internal/dosish.h -fiddle.o: $(hdrdir)/ruby/internal/error.h -fiddle.o: $(hdrdir)/ruby/internal/eval.h -fiddle.o: $(hdrdir)/ruby/internal/event.h -fiddle.o: $(hdrdir)/ruby/internal/fl_type.h -fiddle.o: $(hdrdir)/ruby/internal/gc.h -fiddle.o: $(hdrdir)/ruby/internal/glob.h -fiddle.o: $(hdrdir)/ruby/internal/globals.h -fiddle.o: $(hdrdir)/ruby/internal/has/attribute.h -fiddle.o: $(hdrdir)/ruby/internal/has/builtin.h -fiddle.o: $(hdrdir)/ruby/internal/has/c_attribute.h -fiddle.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h -fiddle.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h -fiddle.o: $(hdrdir)/ruby/internal/has/extension.h -fiddle.o: $(hdrdir)/ruby/internal/has/feature.h -fiddle.o: $(hdrdir)/ruby/internal/has/warning.h -fiddle.o: $(hdrdir)/ruby/internal/intern/array.h -fiddle.o: $(hdrdir)/ruby/internal/intern/bignum.h -fiddle.o: $(hdrdir)/ruby/internal/intern/class.h -fiddle.o: $(hdrdir)/ruby/internal/intern/compar.h -fiddle.o: $(hdrdir)/ruby/internal/intern/complex.h -fiddle.o: $(hdrdir)/ruby/internal/intern/cont.h -fiddle.o: $(hdrdir)/ruby/internal/intern/dir.h -fiddle.o: $(hdrdir)/ruby/internal/intern/enum.h -fiddle.o: $(hdrdir)/ruby/internal/intern/enumerator.h -fiddle.o: $(hdrdir)/ruby/internal/intern/error.h -fiddle.o: $(hdrdir)/ruby/internal/intern/eval.h -fiddle.o: $(hdrdir)/ruby/internal/intern/file.h -fiddle.o: $(hdrdir)/ruby/internal/intern/hash.h -fiddle.o: $(hdrdir)/ruby/internal/intern/io.h -fiddle.o: $(hdrdir)/ruby/internal/intern/load.h -fiddle.o: $(hdrdir)/ruby/internal/intern/marshal.h -fiddle.o: $(hdrdir)/ruby/internal/intern/numeric.h -fiddle.o: $(hdrdir)/ruby/internal/intern/object.h -fiddle.o: $(hdrdir)/ruby/internal/intern/parse.h -fiddle.o: $(hdrdir)/ruby/internal/intern/proc.h -fiddle.o: $(hdrdir)/ruby/internal/intern/process.h -fiddle.o: $(hdrdir)/ruby/internal/intern/random.h -fiddle.o: $(hdrdir)/ruby/internal/intern/range.h -fiddle.o: $(hdrdir)/ruby/internal/intern/rational.h -fiddle.o: $(hdrdir)/ruby/internal/intern/re.h -fiddle.o: $(hdrdir)/ruby/internal/intern/ruby.h -fiddle.o: $(hdrdir)/ruby/internal/intern/select.h -fiddle.o: $(hdrdir)/ruby/internal/intern/select/largesize.h -fiddle.o: $(hdrdir)/ruby/internal/intern/signal.h -fiddle.o: $(hdrdir)/ruby/internal/intern/sprintf.h -fiddle.o: $(hdrdir)/ruby/internal/intern/string.h -fiddle.o: $(hdrdir)/ruby/internal/intern/struct.h -fiddle.o: $(hdrdir)/ruby/internal/intern/thread.h -fiddle.o: $(hdrdir)/ruby/internal/intern/time.h -fiddle.o: $(hdrdir)/ruby/internal/intern/variable.h -fiddle.o: $(hdrdir)/ruby/internal/intern/vm.h -fiddle.o: $(hdrdir)/ruby/internal/interpreter.h -fiddle.o: $(hdrdir)/ruby/internal/iterator.h -fiddle.o: $(hdrdir)/ruby/internal/memory.h -fiddle.o: $(hdrdir)/ruby/internal/method.h -fiddle.o: $(hdrdir)/ruby/internal/module.h -fiddle.o: $(hdrdir)/ruby/internal/newobj.h -fiddle.o: $(hdrdir)/ruby/internal/scan_args.h -fiddle.o: $(hdrdir)/ruby/internal/special_consts.h -fiddle.o: $(hdrdir)/ruby/internal/static_assert.h -fiddle.o: $(hdrdir)/ruby/internal/stdalign.h -fiddle.o: $(hdrdir)/ruby/internal/stdbool.h -fiddle.o: $(hdrdir)/ruby/internal/symbol.h -fiddle.o: $(hdrdir)/ruby/internal/value.h -fiddle.o: $(hdrdir)/ruby/internal/value_type.h -fiddle.o: $(hdrdir)/ruby/internal/variable.h -fiddle.o: $(hdrdir)/ruby/internal/warning_push.h -fiddle.o: $(hdrdir)/ruby/internal/xmalloc.h -fiddle.o: $(hdrdir)/ruby/missing.h -fiddle.o: $(hdrdir)/ruby/ruby.h -fiddle.o: $(hdrdir)/ruby/st.h -fiddle.o: $(hdrdir)/ruby/subst.h -fiddle.o: closure.h -fiddle.o: conversions.h -fiddle.o: fiddle.c -fiddle.o: fiddle.h -fiddle.o: function.h -function.o: $(RUBY_EXTCONF_H) -function.o: $(arch_hdrdir)/ruby/config.h -function.o: $(hdrdir)/ruby.h -function.o: $(hdrdir)/ruby/assert.h -function.o: $(hdrdir)/ruby/backward.h -function.o: $(hdrdir)/ruby/backward/2/assume.h -function.o: $(hdrdir)/ruby/backward/2/attributes.h -function.o: $(hdrdir)/ruby/backward/2/bool.h -function.o: $(hdrdir)/ruby/backward/2/inttypes.h -function.o: $(hdrdir)/ruby/backward/2/limits.h -function.o: $(hdrdir)/ruby/backward/2/long_long.h -function.o: $(hdrdir)/ruby/backward/2/stdalign.h -function.o: $(hdrdir)/ruby/backward/2/stdarg.h -function.o: $(hdrdir)/ruby/defines.h -function.o: $(hdrdir)/ruby/intern.h -function.o: $(hdrdir)/ruby/internal/abi.h -function.o: $(hdrdir)/ruby/internal/anyargs.h -function.o: $(hdrdir)/ruby/internal/arithmetic.h -function.o: $(hdrdir)/ruby/internal/arithmetic/char.h -function.o: $(hdrdir)/ruby/internal/arithmetic/double.h -function.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h -function.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h -function.o: $(hdrdir)/ruby/internal/arithmetic/int.h -function.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h -function.o: $(hdrdir)/ruby/internal/arithmetic/long.h -function.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h -function.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h -function.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h -function.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h -function.o: $(hdrdir)/ruby/internal/arithmetic/short.h -function.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h -function.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h -function.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h -function.o: $(hdrdir)/ruby/internal/assume.h -function.o: $(hdrdir)/ruby/internal/attr/alloc_size.h -function.o: $(hdrdir)/ruby/internal/attr/artificial.h -function.o: $(hdrdir)/ruby/internal/attr/cold.h -function.o: $(hdrdir)/ruby/internal/attr/const.h -function.o: $(hdrdir)/ruby/internal/attr/constexpr.h -function.o: $(hdrdir)/ruby/internal/attr/deprecated.h -function.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h -function.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h -function.o: $(hdrdir)/ruby/internal/attr/error.h -function.o: $(hdrdir)/ruby/internal/attr/flag_enum.h -function.o: $(hdrdir)/ruby/internal/attr/forceinline.h -function.o: $(hdrdir)/ruby/internal/attr/format.h -function.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h -function.o: $(hdrdir)/ruby/internal/attr/noalias.h -function.o: $(hdrdir)/ruby/internal/attr/nodiscard.h -function.o: $(hdrdir)/ruby/internal/attr/noexcept.h -function.o: $(hdrdir)/ruby/internal/attr/noinline.h -function.o: $(hdrdir)/ruby/internal/attr/nonnull.h -function.o: $(hdrdir)/ruby/internal/attr/noreturn.h -function.o: $(hdrdir)/ruby/internal/attr/packed_struct.h -function.o: $(hdrdir)/ruby/internal/attr/pure.h -function.o: $(hdrdir)/ruby/internal/attr/restrict.h -function.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h -function.o: $(hdrdir)/ruby/internal/attr/warning.h -function.o: $(hdrdir)/ruby/internal/attr/weakref.h -function.o: $(hdrdir)/ruby/internal/cast.h -function.o: $(hdrdir)/ruby/internal/compiler_is.h -function.o: $(hdrdir)/ruby/internal/compiler_is/apple.h -function.o: $(hdrdir)/ruby/internal/compiler_is/clang.h -function.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h -function.o: $(hdrdir)/ruby/internal/compiler_is/intel.h -function.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h -function.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h -function.o: $(hdrdir)/ruby/internal/compiler_since.h -function.o: $(hdrdir)/ruby/internal/config.h -function.o: $(hdrdir)/ruby/internal/constant_p.h -function.o: $(hdrdir)/ruby/internal/core.h -function.o: $(hdrdir)/ruby/internal/core/rarray.h -function.o: $(hdrdir)/ruby/internal/core/rbasic.h -function.o: $(hdrdir)/ruby/internal/core/rbignum.h -function.o: $(hdrdir)/ruby/internal/core/rclass.h -function.o: $(hdrdir)/ruby/internal/core/rdata.h -function.o: $(hdrdir)/ruby/internal/core/rfile.h -function.o: $(hdrdir)/ruby/internal/core/rhash.h -function.o: $(hdrdir)/ruby/internal/core/robject.h -function.o: $(hdrdir)/ruby/internal/core/rregexp.h -function.o: $(hdrdir)/ruby/internal/core/rstring.h -function.o: $(hdrdir)/ruby/internal/core/rstruct.h -function.o: $(hdrdir)/ruby/internal/core/rtypeddata.h -function.o: $(hdrdir)/ruby/internal/ctype.h -function.o: $(hdrdir)/ruby/internal/dllexport.h -function.o: $(hdrdir)/ruby/internal/dosish.h -function.o: $(hdrdir)/ruby/internal/error.h -function.o: $(hdrdir)/ruby/internal/eval.h -function.o: $(hdrdir)/ruby/internal/event.h -function.o: $(hdrdir)/ruby/internal/fl_type.h -function.o: $(hdrdir)/ruby/internal/gc.h -function.o: $(hdrdir)/ruby/internal/glob.h -function.o: $(hdrdir)/ruby/internal/globals.h -function.o: $(hdrdir)/ruby/internal/has/attribute.h -function.o: $(hdrdir)/ruby/internal/has/builtin.h -function.o: $(hdrdir)/ruby/internal/has/c_attribute.h -function.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h -function.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h -function.o: $(hdrdir)/ruby/internal/has/extension.h -function.o: $(hdrdir)/ruby/internal/has/feature.h -function.o: $(hdrdir)/ruby/internal/has/warning.h -function.o: $(hdrdir)/ruby/internal/intern/array.h -function.o: $(hdrdir)/ruby/internal/intern/bignum.h -function.o: $(hdrdir)/ruby/internal/intern/class.h -function.o: $(hdrdir)/ruby/internal/intern/compar.h -function.o: $(hdrdir)/ruby/internal/intern/complex.h -function.o: $(hdrdir)/ruby/internal/intern/cont.h -function.o: $(hdrdir)/ruby/internal/intern/dir.h -function.o: $(hdrdir)/ruby/internal/intern/enum.h -function.o: $(hdrdir)/ruby/internal/intern/enumerator.h -function.o: $(hdrdir)/ruby/internal/intern/error.h -function.o: $(hdrdir)/ruby/internal/intern/eval.h -function.o: $(hdrdir)/ruby/internal/intern/file.h -function.o: $(hdrdir)/ruby/internal/intern/hash.h -function.o: $(hdrdir)/ruby/internal/intern/io.h -function.o: $(hdrdir)/ruby/internal/intern/load.h -function.o: $(hdrdir)/ruby/internal/intern/marshal.h -function.o: $(hdrdir)/ruby/internal/intern/numeric.h -function.o: $(hdrdir)/ruby/internal/intern/object.h -function.o: $(hdrdir)/ruby/internal/intern/parse.h -function.o: $(hdrdir)/ruby/internal/intern/proc.h -function.o: $(hdrdir)/ruby/internal/intern/process.h -function.o: $(hdrdir)/ruby/internal/intern/random.h -function.o: $(hdrdir)/ruby/internal/intern/range.h -function.o: $(hdrdir)/ruby/internal/intern/rational.h -function.o: $(hdrdir)/ruby/internal/intern/re.h -function.o: $(hdrdir)/ruby/internal/intern/ruby.h -function.o: $(hdrdir)/ruby/internal/intern/select.h -function.o: $(hdrdir)/ruby/internal/intern/select/largesize.h -function.o: $(hdrdir)/ruby/internal/intern/signal.h -function.o: $(hdrdir)/ruby/internal/intern/sprintf.h -function.o: $(hdrdir)/ruby/internal/intern/string.h -function.o: $(hdrdir)/ruby/internal/intern/struct.h -function.o: $(hdrdir)/ruby/internal/intern/thread.h -function.o: $(hdrdir)/ruby/internal/intern/time.h -function.o: $(hdrdir)/ruby/internal/intern/variable.h -function.o: $(hdrdir)/ruby/internal/intern/vm.h -function.o: $(hdrdir)/ruby/internal/interpreter.h -function.o: $(hdrdir)/ruby/internal/iterator.h -function.o: $(hdrdir)/ruby/internal/memory.h -function.o: $(hdrdir)/ruby/internal/method.h -function.o: $(hdrdir)/ruby/internal/module.h -function.o: $(hdrdir)/ruby/internal/newobj.h -function.o: $(hdrdir)/ruby/internal/scan_args.h -function.o: $(hdrdir)/ruby/internal/special_consts.h -function.o: $(hdrdir)/ruby/internal/static_assert.h -function.o: $(hdrdir)/ruby/internal/stdalign.h -function.o: $(hdrdir)/ruby/internal/stdbool.h -function.o: $(hdrdir)/ruby/internal/symbol.h -function.o: $(hdrdir)/ruby/internal/value.h -function.o: $(hdrdir)/ruby/internal/value_type.h -function.o: $(hdrdir)/ruby/internal/variable.h -function.o: $(hdrdir)/ruby/internal/warning_push.h -function.o: $(hdrdir)/ruby/internal/xmalloc.h -function.o: $(hdrdir)/ruby/missing.h -function.o: $(hdrdir)/ruby/ruby.h -function.o: $(hdrdir)/ruby/st.h -function.o: $(hdrdir)/ruby/subst.h -function.o: $(hdrdir)/ruby/thread.h -function.o: closure.h -function.o: conversions.h -function.o: fiddle.h -function.o: function.c -function.o: function.h -handle.o: $(RUBY_EXTCONF_H) -handle.o: $(arch_hdrdir)/ruby/config.h -handle.o: $(hdrdir)/ruby.h -handle.o: $(hdrdir)/ruby/assert.h -handle.o: $(hdrdir)/ruby/backward.h -handle.o: $(hdrdir)/ruby/backward/2/assume.h -handle.o: $(hdrdir)/ruby/backward/2/attributes.h -handle.o: $(hdrdir)/ruby/backward/2/bool.h -handle.o: $(hdrdir)/ruby/backward/2/inttypes.h -handle.o: $(hdrdir)/ruby/backward/2/limits.h -handle.o: $(hdrdir)/ruby/backward/2/long_long.h -handle.o: $(hdrdir)/ruby/backward/2/stdalign.h -handle.o: $(hdrdir)/ruby/backward/2/stdarg.h -handle.o: $(hdrdir)/ruby/defines.h -handle.o: $(hdrdir)/ruby/intern.h -handle.o: $(hdrdir)/ruby/internal/abi.h -handle.o: $(hdrdir)/ruby/internal/anyargs.h -handle.o: $(hdrdir)/ruby/internal/arithmetic.h -handle.o: $(hdrdir)/ruby/internal/arithmetic/char.h -handle.o: $(hdrdir)/ruby/internal/arithmetic/double.h -handle.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h -handle.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h -handle.o: $(hdrdir)/ruby/internal/arithmetic/int.h -handle.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h -handle.o: $(hdrdir)/ruby/internal/arithmetic/long.h -handle.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h -handle.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h -handle.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h -handle.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h -handle.o: $(hdrdir)/ruby/internal/arithmetic/short.h -handle.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h -handle.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h -handle.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h -handle.o: $(hdrdir)/ruby/internal/assume.h -handle.o: $(hdrdir)/ruby/internal/attr/alloc_size.h -handle.o: $(hdrdir)/ruby/internal/attr/artificial.h -handle.o: $(hdrdir)/ruby/internal/attr/cold.h -handle.o: $(hdrdir)/ruby/internal/attr/const.h -handle.o: $(hdrdir)/ruby/internal/attr/constexpr.h -handle.o: $(hdrdir)/ruby/internal/attr/deprecated.h -handle.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h -handle.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h -handle.o: $(hdrdir)/ruby/internal/attr/error.h -handle.o: $(hdrdir)/ruby/internal/attr/flag_enum.h -handle.o: $(hdrdir)/ruby/internal/attr/forceinline.h -handle.o: $(hdrdir)/ruby/internal/attr/format.h -handle.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h -handle.o: $(hdrdir)/ruby/internal/attr/noalias.h -handle.o: $(hdrdir)/ruby/internal/attr/nodiscard.h -handle.o: $(hdrdir)/ruby/internal/attr/noexcept.h -handle.o: $(hdrdir)/ruby/internal/attr/noinline.h -handle.o: $(hdrdir)/ruby/internal/attr/nonnull.h -handle.o: $(hdrdir)/ruby/internal/attr/noreturn.h -handle.o: $(hdrdir)/ruby/internal/attr/packed_struct.h -handle.o: $(hdrdir)/ruby/internal/attr/pure.h -handle.o: $(hdrdir)/ruby/internal/attr/restrict.h -handle.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h -handle.o: $(hdrdir)/ruby/internal/attr/warning.h -handle.o: $(hdrdir)/ruby/internal/attr/weakref.h -handle.o: $(hdrdir)/ruby/internal/cast.h -handle.o: $(hdrdir)/ruby/internal/compiler_is.h -handle.o: $(hdrdir)/ruby/internal/compiler_is/apple.h -handle.o: $(hdrdir)/ruby/internal/compiler_is/clang.h -handle.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h -handle.o: $(hdrdir)/ruby/internal/compiler_is/intel.h -handle.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h -handle.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h -handle.o: $(hdrdir)/ruby/internal/compiler_since.h -handle.o: $(hdrdir)/ruby/internal/config.h -handle.o: $(hdrdir)/ruby/internal/constant_p.h -handle.o: $(hdrdir)/ruby/internal/core.h -handle.o: $(hdrdir)/ruby/internal/core/rarray.h -handle.o: $(hdrdir)/ruby/internal/core/rbasic.h -handle.o: $(hdrdir)/ruby/internal/core/rbignum.h -handle.o: $(hdrdir)/ruby/internal/core/rclass.h -handle.o: $(hdrdir)/ruby/internal/core/rdata.h -handle.o: $(hdrdir)/ruby/internal/core/rfile.h -handle.o: $(hdrdir)/ruby/internal/core/rhash.h -handle.o: $(hdrdir)/ruby/internal/core/robject.h -handle.o: $(hdrdir)/ruby/internal/core/rregexp.h -handle.o: $(hdrdir)/ruby/internal/core/rstring.h -handle.o: $(hdrdir)/ruby/internal/core/rstruct.h -handle.o: $(hdrdir)/ruby/internal/core/rtypeddata.h -handle.o: $(hdrdir)/ruby/internal/ctype.h -handle.o: $(hdrdir)/ruby/internal/dllexport.h -handle.o: $(hdrdir)/ruby/internal/dosish.h -handle.o: $(hdrdir)/ruby/internal/error.h -handle.o: $(hdrdir)/ruby/internal/eval.h -handle.o: $(hdrdir)/ruby/internal/event.h -handle.o: $(hdrdir)/ruby/internal/fl_type.h -handle.o: $(hdrdir)/ruby/internal/gc.h -handle.o: $(hdrdir)/ruby/internal/glob.h -handle.o: $(hdrdir)/ruby/internal/globals.h -handle.o: $(hdrdir)/ruby/internal/has/attribute.h -handle.o: $(hdrdir)/ruby/internal/has/builtin.h -handle.o: $(hdrdir)/ruby/internal/has/c_attribute.h -handle.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h -handle.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h -handle.o: $(hdrdir)/ruby/internal/has/extension.h -handle.o: $(hdrdir)/ruby/internal/has/feature.h -handle.o: $(hdrdir)/ruby/internal/has/warning.h -handle.o: $(hdrdir)/ruby/internal/intern/array.h -handle.o: $(hdrdir)/ruby/internal/intern/bignum.h -handle.o: $(hdrdir)/ruby/internal/intern/class.h -handle.o: $(hdrdir)/ruby/internal/intern/compar.h -handle.o: $(hdrdir)/ruby/internal/intern/complex.h -handle.o: $(hdrdir)/ruby/internal/intern/cont.h -handle.o: $(hdrdir)/ruby/internal/intern/dir.h -handle.o: $(hdrdir)/ruby/internal/intern/enum.h -handle.o: $(hdrdir)/ruby/internal/intern/enumerator.h -handle.o: $(hdrdir)/ruby/internal/intern/error.h -handle.o: $(hdrdir)/ruby/internal/intern/eval.h -handle.o: $(hdrdir)/ruby/internal/intern/file.h -handle.o: $(hdrdir)/ruby/internal/intern/hash.h -handle.o: $(hdrdir)/ruby/internal/intern/io.h -handle.o: $(hdrdir)/ruby/internal/intern/load.h -handle.o: $(hdrdir)/ruby/internal/intern/marshal.h -handle.o: $(hdrdir)/ruby/internal/intern/numeric.h -handle.o: $(hdrdir)/ruby/internal/intern/object.h -handle.o: $(hdrdir)/ruby/internal/intern/parse.h -handle.o: $(hdrdir)/ruby/internal/intern/proc.h -handle.o: $(hdrdir)/ruby/internal/intern/process.h -handle.o: $(hdrdir)/ruby/internal/intern/random.h -handle.o: $(hdrdir)/ruby/internal/intern/range.h -handle.o: $(hdrdir)/ruby/internal/intern/rational.h -handle.o: $(hdrdir)/ruby/internal/intern/re.h -handle.o: $(hdrdir)/ruby/internal/intern/ruby.h -handle.o: $(hdrdir)/ruby/internal/intern/select.h -handle.o: $(hdrdir)/ruby/internal/intern/select/largesize.h -handle.o: $(hdrdir)/ruby/internal/intern/signal.h -handle.o: $(hdrdir)/ruby/internal/intern/sprintf.h -handle.o: $(hdrdir)/ruby/internal/intern/string.h -handle.o: $(hdrdir)/ruby/internal/intern/struct.h -handle.o: $(hdrdir)/ruby/internal/intern/thread.h -handle.o: $(hdrdir)/ruby/internal/intern/time.h -handle.o: $(hdrdir)/ruby/internal/intern/variable.h -handle.o: $(hdrdir)/ruby/internal/intern/vm.h -handle.o: $(hdrdir)/ruby/internal/interpreter.h -handle.o: $(hdrdir)/ruby/internal/iterator.h -handle.o: $(hdrdir)/ruby/internal/memory.h -handle.o: $(hdrdir)/ruby/internal/method.h -handle.o: $(hdrdir)/ruby/internal/module.h -handle.o: $(hdrdir)/ruby/internal/newobj.h -handle.o: $(hdrdir)/ruby/internal/scan_args.h -handle.o: $(hdrdir)/ruby/internal/special_consts.h -handle.o: $(hdrdir)/ruby/internal/static_assert.h -handle.o: $(hdrdir)/ruby/internal/stdalign.h -handle.o: $(hdrdir)/ruby/internal/stdbool.h -handle.o: $(hdrdir)/ruby/internal/symbol.h -handle.o: $(hdrdir)/ruby/internal/value.h -handle.o: $(hdrdir)/ruby/internal/value_type.h -handle.o: $(hdrdir)/ruby/internal/variable.h -handle.o: $(hdrdir)/ruby/internal/warning_push.h -handle.o: $(hdrdir)/ruby/internal/xmalloc.h -handle.o: $(hdrdir)/ruby/missing.h -handle.o: $(hdrdir)/ruby/ruby.h -handle.o: $(hdrdir)/ruby/st.h -handle.o: $(hdrdir)/ruby/subst.h -handle.o: closure.h -handle.o: conversions.h -handle.o: fiddle.h -handle.o: function.h -handle.o: handle.c -memory_view.o: $(RUBY_EXTCONF_H) -memory_view.o: $(arch_hdrdir)/ruby/config.h -memory_view.o: $(hdrdir)/ruby.h -memory_view.o: $(hdrdir)/ruby/assert.h -memory_view.o: $(hdrdir)/ruby/backward.h -memory_view.o: $(hdrdir)/ruby/backward/2/assume.h -memory_view.o: $(hdrdir)/ruby/backward/2/attributes.h -memory_view.o: $(hdrdir)/ruby/backward/2/bool.h -memory_view.o: $(hdrdir)/ruby/backward/2/inttypes.h -memory_view.o: $(hdrdir)/ruby/backward/2/limits.h -memory_view.o: $(hdrdir)/ruby/backward/2/long_long.h -memory_view.o: $(hdrdir)/ruby/backward/2/stdalign.h -memory_view.o: $(hdrdir)/ruby/backward/2/stdarg.h -memory_view.o: $(hdrdir)/ruby/defines.h -memory_view.o: $(hdrdir)/ruby/encoding.h -memory_view.o: $(hdrdir)/ruby/intern.h -memory_view.o: $(hdrdir)/ruby/internal/abi.h -memory_view.o: $(hdrdir)/ruby/internal/anyargs.h -memory_view.o: $(hdrdir)/ruby/internal/arithmetic.h -memory_view.o: $(hdrdir)/ruby/internal/arithmetic/char.h -memory_view.o: $(hdrdir)/ruby/internal/arithmetic/double.h -memory_view.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h -memory_view.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h -memory_view.o: $(hdrdir)/ruby/internal/arithmetic/int.h -memory_view.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h -memory_view.o: $(hdrdir)/ruby/internal/arithmetic/long.h -memory_view.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h -memory_view.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h -memory_view.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h -memory_view.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h -memory_view.o: $(hdrdir)/ruby/internal/arithmetic/short.h -memory_view.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h -memory_view.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h -memory_view.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h -memory_view.o: $(hdrdir)/ruby/internal/assume.h -memory_view.o: $(hdrdir)/ruby/internal/attr/alloc_size.h -memory_view.o: $(hdrdir)/ruby/internal/attr/artificial.h -memory_view.o: $(hdrdir)/ruby/internal/attr/cold.h -memory_view.o: $(hdrdir)/ruby/internal/attr/const.h -memory_view.o: $(hdrdir)/ruby/internal/attr/constexpr.h -memory_view.o: $(hdrdir)/ruby/internal/attr/deprecated.h -memory_view.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h -memory_view.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h -memory_view.o: $(hdrdir)/ruby/internal/attr/error.h -memory_view.o: $(hdrdir)/ruby/internal/attr/flag_enum.h -memory_view.o: $(hdrdir)/ruby/internal/attr/forceinline.h -memory_view.o: $(hdrdir)/ruby/internal/attr/format.h -memory_view.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h -memory_view.o: $(hdrdir)/ruby/internal/attr/noalias.h -memory_view.o: $(hdrdir)/ruby/internal/attr/nodiscard.h -memory_view.o: $(hdrdir)/ruby/internal/attr/noexcept.h -memory_view.o: $(hdrdir)/ruby/internal/attr/noinline.h -memory_view.o: $(hdrdir)/ruby/internal/attr/nonnull.h -memory_view.o: $(hdrdir)/ruby/internal/attr/noreturn.h -memory_view.o: $(hdrdir)/ruby/internal/attr/packed_struct.h -memory_view.o: $(hdrdir)/ruby/internal/attr/pure.h -memory_view.o: $(hdrdir)/ruby/internal/attr/restrict.h -memory_view.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h -memory_view.o: $(hdrdir)/ruby/internal/attr/warning.h -memory_view.o: $(hdrdir)/ruby/internal/attr/weakref.h -memory_view.o: $(hdrdir)/ruby/internal/cast.h -memory_view.o: $(hdrdir)/ruby/internal/compiler_is.h -memory_view.o: $(hdrdir)/ruby/internal/compiler_is/apple.h -memory_view.o: $(hdrdir)/ruby/internal/compiler_is/clang.h -memory_view.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h -memory_view.o: $(hdrdir)/ruby/internal/compiler_is/intel.h -memory_view.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h -memory_view.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h -memory_view.o: $(hdrdir)/ruby/internal/compiler_since.h -memory_view.o: $(hdrdir)/ruby/internal/config.h -memory_view.o: $(hdrdir)/ruby/internal/constant_p.h -memory_view.o: $(hdrdir)/ruby/internal/core.h -memory_view.o: $(hdrdir)/ruby/internal/core/rarray.h -memory_view.o: $(hdrdir)/ruby/internal/core/rbasic.h -memory_view.o: $(hdrdir)/ruby/internal/core/rbignum.h -memory_view.o: $(hdrdir)/ruby/internal/core/rclass.h -memory_view.o: $(hdrdir)/ruby/internal/core/rdata.h -memory_view.o: $(hdrdir)/ruby/internal/core/rfile.h -memory_view.o: $(hdrdir)/ruby/internal/core/rhash.h -memory_view.o: $(hdrdir)/ruby/internal/core/robject.h -memory_view.o: $(hdrdir)/ruby/internal/core/rregexp.h -memory_view.o: $(hdrdir)/ruby/internal/core/rstring.h -memory_view.o: $(hdrdir)/ruby/internal/core/rstruct.h -memory_view.o: $(hdrdir)/ruby/internal/core/rtypeddata.h -memory_view.o: $(hdrdir)/ruby/internal/ctype.h -memory_view.o: $(hdrdir)/ruby/internal/dllexport.h -memory_view.o: $(hdrdir)/ruby/internal/dosish.h -memory_view.o: $(hdrdir)/ruby/internal/encoding/coderange.h -memory_view.o: $(hdrdir)/ruby/internal/encoding/ctype.h -memory_view.o: $(hdrdir)/ruby/internal/encoding/encoding.h -memory_view.o: $(hdrdir)/ruby/internal/encoding/pathname.h -memory_view.o: $(hdrdir)/ruby/internal/encoding/re.h -memory_view.o: $(hdrdir)/ruby/internal/encoding/sprintf.h -memory_view.o: $(hdrdir)/ruby/internal/encoding/string.h -memory_view.o: $(hdrdir)/ruby/internal/encoding/symbol.h -memory_view.o: $(hdrdir)/ruby/internal/encoding/transcode.h -memory_view.o: $(hdrdir)/ruby/internal/error.h -memory_view.o: $(hdrdir)/ruby/internal/eval.h -memory_view.o: $(hdrdir)/ruby/internal/event.h -memory_view.o: $(hdrdir)/ruby/internal/fl_type.h -memory_view.o: $(hdrdir)/ruby/internal/gc.h -memory_view.o: $(hdrdir)/ruby/internal/glob.h -memory_view.o: $(hdrdir)/ruby/internal/globals.h -memory_view.o: $(hdrdir)/ruby/internal/has/attribute.h -memory_view.o: $(hdrdir)/ruby/internal/has/builtin.h -memory_view.o: $(hdrdir)/ruby/internal/has/c_attribute.h -memory_view.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h -memory_view.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h -memory_view.o: $(hdrdir)/ruby/internal/has/extension.h -memory_view.o: $(hdrdir)/ruby/internal/has/feature.h -memory_view.o: $(hdrdir)/ruby/internal/has/warning.h -memory_view.o: $(hdrdir)/ruby/internal/intern/array.h -memory_view.o: $(hdrdir)/ruby/internal/intern/bignum.h -memory_view.o: $(hdrdir)/ruby/internal/intern/class.h -memory_view.o: $(hdrdir)/ruby/internal/intern/compar.h -memory_view.o: $(hdrdir)/ruby/internal/intern/complex.h -memory_view.o: $(hdrdir)/ruby/internal/intern/cont.h -memory_view.o: $(hdrdir)/ruby/internal/intern/dir.h -memory_view.o: $(hdrdir)/ruby/internal/intern/enum.h -memory_view.o: $(hdrdir)/ruby/internal/intern/enumerator.h -memory_view.o: $(hdrdir)/ruby/internal/intern/error.h -memory_view.o: $(hdrdir)/ruby/internal/intern/eval.h -memory_view.o: $(hdrdir)/ruby/internal/intern/file.h -memory_view.o: $(hdrdir)/ruby/internal/intern/hash.h -memory_view.o: $(hdrdir)/ruby/internal/intern/io.h -memory_view.o: $(hdrdir)/ruby/internal/intern/load.h -memory_view.o: $(hdrdir)/ruby/internal/intern/marshal.h -memory_view.o: $(hdrdir)/ruby/internal/intern/numeric.h -memory_view.o: $(hdrdir)/ruby/internal/intern/object.h -memory_view.o: $(hdrdir)/ruby/internal/intern/parse.h -memory_view.o: $(hdrdir)/ruby/internal/intern/proc.h -memory_view.o: $(hdrdir)/ruby/internal/intern/process.h -memory_view.o: $(hdrdir)/ruby/internal/intern/random.h -memory_view.o: $(hdrdir)/ruby/internal/intern/range.h -memory_view.o: $(hdrdir)/ruby/internal/intern/rational.h -memory_view.o: $(hdrdir)/ruby/internal/intern/re.h -memory_view.o: $(hdrdir)/ruby/internal/intern/ruby.h -memory_view.o: $(hdrdir)/ruby/internal/intern/select.h -memory_view.o: $(hdrdir)/ruby/internal/intern/select/largesize.h -memory_view.o: $(hdrdir)/ruby/internal/intern/signal.h -memory_view.o: $(hdrdir)/ruby/internal/intern/sprintf.h -memory_view.o: $(hdrdir)/ruby/internal/intern/string.h -memory_view.o: $(hdrdir)/ruby/internal/intern/struct.h -memory_view.o: $(hdrdir)/ruby/internal/intern/thread.h -memory_view.o: $(hdrdir)/ruby/internal/intern/time.h -memory_view.o: $(hdrdir)/ruby/internal/intern/variable.h -memory_view.o: $(hdrdir)/ruby/internal/intern/vm.h -memory_view.o: $(hdrdir)/ruby/internal/interpreter.h -memory_view.o: $(hdrdir)/ruby/internal/iterator.h -memory_view.o: $(hdrdir)/ruby/internal/memory.h -memory_view.o: $(hdrdir)/ruby/internal/method.h -memory_view.o: $(hdrdir)/ruby/internal/module.h -memory_view.o: $(hdrdir)/ruby/internal/newobj.h -memory_view.o: $(hdrdir)/ruby/internal/scan_args.h -memory_view.o: $(hdrdir)/ruby/internal/special_consts.h -memory_view.o: $(hdrdir)/ruby/internal/static_assert.h -memory_view.o: $(hdrdir)/ruby/internal/stdalign.h -memory_view.o: $(hdrdir)/ruby/internal/stdbool.h -memory_view.o: $(hdrdir)/ruby/internal/symbol.h -memory_view.o: $(hdrdir)/ruby/internal/value.h -memory_view.o: $(hdrdir)/ruby/internal/value_type.h -memory_view.o: $(hdrdir)/ruby/internal/variable.h -memory_view.o: $(hdrdir)/ruby/internal/warning_push.h -memory_view.o: $(hdrdir)/ruby/internal/xmalloc.h -memory_view.o: $(hdrdir)/ruby/memory_view.h -memory_view.o: $(hdrdir)/ruby/missing.h -memory_view.o: $(hdrdir)/ruby/onigmo.h -memory_view.o: $(hdrdir)/ruby/oniguruma.h -memory_view.o: $(hdrdir)/ruby/ruby.h -memory_view.o: $(hdrdir)/ruby/st.h -memory_view.o: $(hdrdir)/ruby/subst.h -memory_view.o: closure.h -memory_view.o: conversions.h -memory_view.o: fiddle.h -memory_view.o: function.h -memory_view.o: memory_view.c -pinned.o: $(RUBY_EXTCONF_H) -pinned.o: $(arch_hdrdir)/ruby/config.h -pinned.o: $(hdrdir)/ruby.h -pinned.o: $(hdrdir)/ruby/assert.h -pinned.o: $(hdrdir)/ruby/backward.h -pinned.o: $(hdrdir)/ruby/backward/2/assume.h -pinned.o: $(hdrdir)/ruby/backward/2/attributes.h -pinned.o: $(hdrdir)/ruby/backward/2/bool.h -pinned.o: $(hdrdir)/ruby/backward/2/inttypes.h -pinned.o: $(hdrdir)/ruby/backward/2/limits.h -pinned.o: $(hdrdir)/ruby/backward/2/long_long.h -pinned.o: $(hdrdir)/ruby/backward/2/stdalign.h -pinned.o: $(hdrdir)/ruby/backward/2/stdarg.h -pinned.o: $(hdrdir)/ruby/defines.h -pinned.o: $(hdrdir)/ruby/intern.h -pinned.o: $(hdrdir)/ruby/internal/abi.h -pinned.o: $(hdrdir)/ruby/internal/anyargs.h -pinned.o: $(hdrdir)/ruby/internal/arithmetic.h -pinned.o: $(hdrdir)/ruby/internal/arithmetic/char.h -pinned.o: $(hdrdir)/ruby/internal/arithmetic/double.h -pinned.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h -pinned.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h -pinned.o: $(hdrdir)/ruby/internal/arithmetic/int.h -pinned.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h -pinned.o: $(hdrdir)/ruby/internal/arithmetic/long.h -pinned.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h -pinned.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h -pinned.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h -pinned.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h -pinned.o: $(hdrdir)/ruby/internal/arithmetic/short.h -pinned.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h -pinned.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h -pinned.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h -pinned.o: $(hdrdir)/ruby/internal/assume.h -pinned.o: $(hdrdir)/ruby/internal/attr/alloc_size.h -pinned.o: $(hdrdir)/ruby/internal/attr/artificial.h -pinned.o: $(hdrdir)/ruby/internal/attr/cold.h -pinned.o: $(hdrdir)/ruby/internal/attr/const.h -pinned.o: $(hdrdir)/ruby/internal/attr/constexpr.h -pinned.o: $(hdrdir)/ruby/internal/attr/deprecated.h -pinned.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h -pinned.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h -pinned.o: $(hdrdir)/ruby/internal/attr/error.h -pinned.o: $(hdrdir)/ruby/internal/attr/flag_enum.h -pinned.o: $(hdrdir)/ruby/internal/attr/forceinline.h -pinned.o: $(hdrdir)/ruby/internal/attr/format.h -pinned.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h -pinned.o: $(hdrdir)/ruby/internal/attr/noalias.h -pinned.o: $(hdrdir)/ruby/internal/attr/nodiscard.h -pinned.o: $(hdrdir)/ruby/internal/attr/noexcept.h -pinned.o: $(hdrdir)/ruby/internal/attr/noinline.h -pinned.o: $(hdrdir)/ruby/internal/attr/nonnull.h -pinned.o: $(hdrdir)/ruby/internal/attr/noreturn.h -pinned.o: $(hdrdir)/ruby/internal/attr/packed_struct.h -pinned.o: $(hdrdir)/ruby/internal/attr/pure.h -pinned.o: $(hdrdir)/ruby/internal/attr/restrict.h -pinned.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h -pinned.o: $(hdrdir)/ruby/internal/attr/warning.h -pinned.o: $(hdrdir)/ruby/internal/attr/weakref.h -pinned.o: $(hdrdir)/ruby/internal/cast.h -pinned.o: $(hdrdir)/ruby/internal/compiler_is.h -pinned.o: $(hdrdir)/ruby/internal/compiler_is/apple.h -pinned.o: $(hdrdir)/ruby/internal/compiler_is/clang.h -pinned.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h -pinned.o: $(hdrdir)/ruby/internal/compiler_is/intel.h -pinned.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h -pinned.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h -pinned.o: $(hdrdir)/ruby/internal/compiler_since.h -pinned.o: $(hdrdir)/ruby/internal/config.h -pinned.o: $(hdrdir)/ruby/internal/constant_p.h -pinned.o: $(hdrdir)/ruby/internal/core.h -pinned.o: $(hdrdir)/ruby/internal/core/rarray.h -pinned.o: $(hdrdir)/ruby/internal/core/rbasic.h -pinned.o: $(hdrdir)/ruby/internal/core/rbignum.h -pinned.o: $(hdrdir)/ruby/internal/core/rclass.h -pinned.o: $(hdrdir)/ruby/internal/core/rdata.h -pinned.o: $(hdrdir)/ruby/internal/core/rfile.h -pinned.o: $(hdrdir)/ruby/internal/core/rhash.h -pinned.o: $(hdrdir)/ruby/internal/core/robject.h -pinned.o: $(hdrdir)/ruby/internal/core/rregexp.h -pinned.o: $(hdrdir)/ruby/internal/core/rstring.h -pinned.o: $(hdrdir)/ruby/internal/core/rstruct.h -pinned.o: $(hdrdir)/ruby/internal/core/rtypeddata.h -pinned.o: $(hdrdir)/ruby/internal/ctype.h -pinned.o: $(hdrdir)/ruby/internal/dllexport.h -pinned.o: $(hdrdir)/ruby/internal/dosish.h -pinned.o: $(hdrdir)/ruby/internal/error.h -pinned.o: $(hdrdir)/ruby/internal/eval.h -pinned.o: $(hdrdir)/ruby/internal/event.h -pinned.o: $(hdrdir)/ruby/internal/fl_type.h -pinned.o: $(hdrdir)/ruby/internal/gc.h -pinned.o: $(hdrdir)/ruby/internal/glob.h -pinned.o: $(hdrdir)/ruby/internal/globals.h -pinned.o: $(hdrdir)/ruby/internal/has/attribute.h -pinned.o: $(hdrdir)/ruby/internal/has/builtin.h -pinned.o: $(hdrdir)/ruby/internal/has/c_attribute.h -pinned.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h -pinned.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h -pinned.o: $(hdrdir)/ruby/internal/has/extension.h -pinned.o: $(hdrdir)/ruby/internal/has/feature.h -pinned.o: $(hdrdir)/ruby/internal/has/warning.h -pinned.o: $(hdrdir)/ruby/internal/intern/array.h -pinned.o: $(hdrdir)/ruby/internal/intern/bignum.h -pinned.o: $(hdrdir)/ruby/internal/intern/class.h -pinned.o: $(hdrdir)/ruby/internal/intern/compar.h -pinned.o: $(hdrdir)/ruby/internal/intern/complex.h -pinned.o: $(hdrdir)/ruby/internal/intern/cont.h -pinned.o: $(hdrdir)/ruby/internal/intern/dir.h -pinned.o: $(hdrdir)/ruby/internal/intern/enum.h -pinned.o: $(hdrdir)/ruby/internal/intern/enumerator.h -pinned.o: $(hdrdir)/ruby/internal/intern/error.h -pinned.o: $(hdrdir)/ruby/internal/intern/eval.h -pinned.o: $(hdrdir)/ruby/internal/intern/file.h -pinned.o: $(hdrdir)/ruby/internal/intern/hash.h -pinned.o: $(hdrdir)/ruby/internal/intern/io.h -pinned.o: $(hdrdir)/ruby/internal/intern/load.h -pinned.o: $(hdrdir)/ruby/internal/intern/marshal.h -pinned.o: $(hdrdir)/ruby/internal/intern/numeric.h -pinned.o: $(hdrdir)/ruby/internal/intern/object.h -pinned.o: $(hdrdir)/ruby/internal/intern/parse.h -pinned.o: $(hdrdir)/ruby/internal/intern/proc.h -pinned.o: $(hdrdir)/ruby/internal/intern/process.h -pinned.o: $(hdrdir)/ruby/internal/intern/random.h -pinned.o: $(hdrdir)/ruby/internal/intern/range.h -pinned.o: $(hdrdir)/ruby/internal/intern/rational.h -pinned.o: $(hdrdir)/ruby/internal/intern/re.h -pinned.o: $(hdrdir)/ruby/internal/intern/ruby.h -pinned.o: $(hdrdir)/ruby/internal/intern/select.h -pinned.o: $(hdrdir)/ruby/internal/intern/select/largesize.h -pinned.o: $(hdrdir)/ruby/internal/intern/signal.h -pinned.o: $(hdrdir)/ruby/internal/intern/sprintf.h -pinned.o: $(hdrdir)/ruby/internal/intern/string.h -pinned.o: $(hdrdir)/ruby/internal/intern/struct.h -pinned.o: $(hdrdir)/ruby/internal/intern/thread.h -pinned.o: $(hdrdir)/ruby/internal/intern/time.h -pinned.o: $(hdrdir)/ruby/internal/intern/variable.h -pinned.o: $(hdrdir)/ruby/internal/intern/vm.h -pinned.o: $(hdrdir)/ruby/internal/interpreter.h -pinned.o: $(hdrdir)/ruby/internal/iterator.h -pinned.o: $(hdrdir)/ruby/internal/memory.h -pinned.o: $(hdrdir)/ruby/internal/method.h -pinned.o: $(hdrdir)/ruby/internal/module.h -pinned.o: $(hdrdir)/ruby/internal/newobj.h -pinned.o: $(hdrdir)/ruby/internal/scan_args.h -pinned.o: $(hdrdir)/ruby/internal/special_consts.h -pinned.o: $(hdrdir)/ruby/internal/static_assert.h -pinned.o: $(hdrdir)/ruby/internal/stdalign.h -pinned.o: $(hdrdir)/ruby/internal/stdbool.h -pinned.o: $(hdrdir)/ruby/internal/symbol.h -pinned.o: $(hdrdir)/ruby/internal/value.h -pinned.o: $(hdrdir)/ruby/internal/value_type.h -pinned.o: $(hdrdir)/ruby/internal/variable.h -pinned.o: $(hdrdir)/ruby/internal/warning_push.h -pinned.o: $(hdrdir)/ruby/internal/xmalloc.h -pinned.o: $(hdrdir)/ruby/missing.h -pinned.o: $(hdrdir)/ruby/ruby.h -pinned.o: $(hdrdir)/ruby/st.h -pinned.o: $(hdrdir)/ruby/subst.h -pinned.o: closure.h -pinned.o: conversions.h -pinned.o: fiddle.h -pinned.o: function.h -pinned.o: pinned.c -pointer.o: $(RUBY_EXTCONF_H) -pointer.o: $(arch_hdrdir)/ruby/config.h -pointer.o: $(hdrdir)/ruby.h -pointer.o: $(hdrdir)/ruby/assert.h -pointer.o: $(hdrdir)/ruby/backward.h -pointer.o: $(hdrdir)/ruby/backward/2/assume.h -pointer.o: $(hdrdir)/ruby/backward/2/attributes.h -pointer.o: $(hdrdir)/ruby/backward/2/bool.h -pointer.o: $(hdrdir)/ruby/backward/2/inttypes.h -pointer.o: $(hdrdir)/ruby/backward/2/limits.h -pointer.o: $(hdrdir)/ruby/backward/2/long_long.h -pointer.o: $(hdrdir)/ruby/backward/2/stdalign.h -pointer.o: $(hdrdir)/ruby/backward/2/stdarg.h -pointer.o: $(hdrdir)/ruby/defines.h -pointer.o: $(hdrdir)/ruby/encoding.h -pointer.o: $(hdrdir)/ruby/intern.h -pointer.o: $(hdrdir)/ruby/internal/abi.h -pointer.o: $(hdrdir)/ruby/internal/anyargs.h -pointer.o: $(hdrdir)/ruby/internal/arithmetic.h -pointer.o: $(hdrdir)/ruby/internal/arithmetic/char.h -pointer.o: $(hdrdir)/ruby/internal/arithmetic/double.h -pointer.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h -pointer.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h -pointer.o: $(hdrdir)/ruby/internal/arithmetic/int.h -pointer.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h -pointer.o: $(hdrdir)/ruby/internal/arithmetic/long.h -pointer.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h -pointer.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h -pointer.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h -pointer.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h -pointer.o: $(hdrdir)/ruby/internal/arithmetic/short.h -pointer.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h -pointer.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h -pointer.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h -pointer.o: $(hdrdir)/ruby/internal/assume.h -pointer.o: $(hdrdir)/ruby/internal/attr/alloc_size.h -pointer.o: $(hdrdir)/ruby/internal/attr/artificial.h -pointer.o: $(hdrdir)/ruby/internal/attr/cold.h -pointer.o: $(hdrdir)/ruby/internal/attr/const.h -pointer.o: $(hdrdir)/ruby/internal/attr/constexpr.h -pointer.o: $(hdrdir)/ruby/internal/attr/deprecated.h -pointer.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h -pointer.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h -pointer.o: $(hdrdir)/ruby/internal/attr/error.h -pointer.o: $(hdrdir)/ruby/internal/attr/flag_enum.h -pointer.o: $(hdrdir)/ruby/internal/attr/forceinline.h -pointer.o: $(hdrdir)/ruby/internal/attr/format.h -pointer.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h -pointer.o: $(hdrdir)/ruby/internal/attr/noalias.h -pointer.o: $(hdrdir)/ruby/internal/attr/nodiscard.h -pointer.o: $(hdrdir)/ruby/internal/attr/noexcept.h -pointer.o: $(hdrdir)/ruby/internal/attr/noinline.h -pointer.o: $(hdrdir)/ruby/internal/attr/nonnull.h -pointer.o: $(hdrdir)/ruby/internal/attr/noreturn.h -pointer.o: $(hdrdir)/ruby/internal/attr/packed_struct.h -pointer.o: $(hdrdir)/ruby/internal/attr/pure.h -pointer.o: $(hdrdir)/ruby/internal/attr/restrict.h -pointer.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h -pointer.o: $(hdrdir)/ruby/internal/attr/warning.h -pointer.o: $(hdrdir)/ruby/internal/attr/weakref.h -pointer.o: $(hdrdir)/ruby/internal/cast.h -pointer.o: $(hdrdir)/ruby/internal/compiler_is.h -pointer.o: $(hdrdir)/ruby/internal/compiler_is/apple.h -pointer.o: $(hdrdir)/ruby/internal/compiler_is/clang.h -pointer.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h -pointer.o: $(hdrdir)/ruby/internal/compiler_is/intel.h -pointer.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h -pointer.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h -pointer.o: $(hdrdir)/ruby/internal/compiler_since.h -pointer.o: $(hdrdir)/ruby/internal/config.h -pointer.o: $(hdrdir)/ruby/internal/constant_p.h -pointer.o: $(hdrdir)/ruby/internal/core.h -pointer.o: $(hdrdir)/ruby/internal/core/rarray.h -pointer.o: $(hdrdir)/ruby/internal/core/rbasic.h -pointer.o: $(hdrdir)/ruby/internal/core/rbignum.h -pointer.o: $(hdrdir)/ruby/internal/core/rclass.h -pointer.o: $(hdrdir)/ruby/internal/core/rdata.h -pointer.o: $(hdrdir)/ruby/internal/core/rfile.h -pointer.o: $(hdrdir)/ruby/internal/core/rhash.h -pointer.o: $(hdrdir)/ruby/internal/core/robject.h -pointer.o: $(hdrdir)/ruby/internal/core/rregexp.h -pointer.o: $(hdrdir)/ruby/internal/core/rstring.h -pointer.o: $(hdrdir)/ruby/internal/core/rstruct.h -pointer.o: $(hdrdir)/ruby/internal/core/rtypeddata.h -pointer.o: $(hdrdir)/ruby/internal/ctype.h -pointer.o: $(hdrdir)/ruby/internal/dllexport.h -pointer.o: $(hdrdir)/ruby/internal/dosish.h -pointer.o: $(hdrdir)/ruby/internal/encoding/coderange.h -pointer.o: $(hdrdir)/ruby/internal/encoding/ctype.h -pointer.o: $(hdrdir)/ruby/internal/encoding/encoding.h -pointer.o: $(hdrdir)/ruby/internal/encoding/pathname.h -pointer.o: $(hdrdir)/ruby/internal/encoding/re.h -pointer.o: $(hdrdir)/ruby/internal/encoding/sprintf.h -pointer.o: $(hdrdir)/ruby/internal/encoding/string.h -pointer.o: $(hdrdir)/ruby/internal/encoding/symbol.h -pointer.o: $(hdrdir)/ruby/internal/encoding/transcode.h -pointer.o: $(hdrdir)/ruby/internal/error.h -pointer.o: $(hdrdir)/ruby/internal/eval.h -pointer.o: $(hdrdir)/ruby/internal/event.h -pointer.o: $(hdrdir)/ruby/internal/fl_type.h -pointer.o: $(hdrdir)/ruby/internal/gc.h -pointer.o: $(hdrdir)/ruby/internal/glob.h -pointer.o: $(hdrdir)/ruby/internal/globals.h -pointer.o: $(hdrdir)/ruby/internal/has/attribute.h -pointer.o: $(hdrdir)/ruby/internal/has/builtin.h -pointer.o: $(hdrdir)/ruby/internal/has/c_attribute.h -pointer.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h -pointer.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h -pointer.o: $(hdrdir)/ruby/internal/has/extension.h -pointer.o: $(hdrdir)/ruby/internal/has/feature.h -pointer.o: $(hdrdir)/ruby/internal/has/warning.h -pointer.o: $(hdrdir)/ruby/internal/intern/array.h -pointer.o: $(hdrdir)/ruby/internal/intern/bignum.h -pointer.o: $(hdrdir)/ruby/internal/intern/class.h -pointer.o: $(hdrdir)/ruby/internal/intern/compar.h -pointer.o: $(hdrdir)/ruby/internal/intern/complex.h -pointer.o: $(hdrdir)/ruby/internal/intern/cont.h -pointer.o: $(hdrdir)/ruby/internal/intern/dir.h -pointer.o: $(hdrdir)/ruby/internal/intern/enum.h -pointer.o: $(hdrdir)/ruby/internal/intern/enumerator.h -pointer.o: $(hdrdir)/ruby/internal/intern/error.h -pointer.o: $(hdrdir)/ruby/internal/intern/eval.h -pointer.o: $(hdrdir)/ruby/internal/intern/file.h -pointer.o: $(hdrdir)/ruby/internal/intern/hash.h -pointer.o: $(hdrdir)/ruby/internal/intern/io.h -pointer.o: $(hdrdir)/ruby/internal/intern/load.h -pointer.o: $(hdrdir)/ruby/internal/intern/marshal.h -pointer.o: $(hdrdir)/ruby/internal/intern/numeric.h -pointer.o: $(hdrdir)/ruby/internal/intern/object.h -pointer.o: $(hdrdir)/ruby/internal/intern/parse.h -pointer.o: $(hdrdir)/ruby/internal/intern/proc.h -pointer.o: $(hdrdir)/ruby/internal/intern/process.h -pointer.o: $(hdrdir)/ruby/internal/intern/random.h -pointer.o: $(hdrdir)/ruby/internal/intern/range.h -pointer.o: $(hdrdir)/ruby/internal/intern/rational.h -pointer.o: $(hdrdir)/ruby/internal/intern/re.h -pointer.o: $(hdrdir)/ruby/internal/intern/ruby.h -pointer.o: $(hdrdir)/ruby/internal/intern/select.h -pointer.o: $(hdrdir)/ruby/internal/intern/select/largesize.h -pointer.o: $(hdrdir)/ruby/internal/intern/signal.h -pointer.o: $(hdrdir)/ruby/internal/intern/sprintf.h -pointer.o: $(hdrdir)/ruby/internal/intern/string.h -pointer.o: $(hdrdir)/ruby/internal/intern/struct.h -pointer.o: $(hdrdir)/ruby/internal/intern/thread.h -pointer.o: $(hdrdir)/ruby/internal/intern/time.h -pointer.o: $(hdrdir)/ruby/internal/intern/variable.h -pointer.o: $(hdrdir)/ruby/internal/intern/vm.h -pointer.o: $(hdrdir)/ruby/internal/interpreter.h -pointer.o: $(hdrdir)/ruby/internal/iterator.h -pointer.o: $(hdrdir)/ruby/internal/memory.h -pointer.o: $(hdrdir)/ruby/internal/method.h -pointer.o: $(hdrdir)/ruby/internal/module.h -pointer.o: $(hdrdir)/ruby/internal/newobj.h -pointer.o: $(hdrdir)/ruby/internal/scan_args.h -pointer.o: $(hdrdir)/ruby/internal/special_consts.h -pointer.o: $(hdrdir)/ruby/internal/static_assert.h -pointer.o: $(hdrdir)/ruby/internal/stdalign.h -pointer.o: $(hdrdir)/ruby/internal/stdbool.h -pointer.o: $(hdrdir)/ruby/internal/symbol.h -pointer.o: $(hdrdir)/ruby/internal/value.h -pointer.o: $(hdrdir)/ruby/internal/value_type.h -pointer.o: $(hdrdir)/ruby/internal/variable.h -pointer.o: $(hdrdir)/ruby/internal/warning_push.h -pointer.o: $(hdrdir)/ruby/internal/xmalloc.h -pointer.o: $(hdrdir)/ruby/io.h -pointer.o: $(hdrdir)/ruby/memory_view.h -pointer.o: $(hdrdir)/ruby/missing.h -pointer.o: $(hdrdir)/ruby/onigmo.h -pointer.o: $(hdrdir)/ruby/oniguruma.h -pointer.o: $(hdrdir)/ruby/ruby.h -pointer.o: $(hdrdir)/ruby/st.h -pointer.o: $(hdrdir)/ruby/subst.h -pointer.o: closure.h -pointer.o: conversions.h -pointer.o: fiddle.h -pointer.o: function.h -pointer.o: pointer.c -# AUTOGENERATED DEPENDENCIES END diff --git a/ext/fiddle/extconf.rb b/ext/fiddle/extconf.rb deleted file mode 100644 index 2d85b3eea5..0000000000 --- a/ext/fiddle/extconf.rb +++ /dev/null @@ -1,256 +0,0 @@ -# frozen_string_literal: true -require 'mkmf' - -# :stopdoc: - -def gcc? - RbConfig::CONFIG["GCC"] == "yes" -end - -def disable_optimization_build_flag(flags) - if gcc? - expanded_flags = RbConfig.expand(flags.dup) - optimization_option_pattern = /(^|\s)?-O\d(\s|$)?/ - if optimization_option_pattern.match?(expanded_flags) - expanded_flags.gsub(optimization_option_pattern, '\\1-Og\\2') - else - flags + " -Og" - end - else - flags - end -end - -def enable_debug_build_flag(flags) - if gcc? - expanded_flags = RbConfig.expand(flags.dup) - debug_option_pattern = /(^|\s)-g(?:gdb)?\d?(\s|$)/ - if debug_option_pattern.match?(expanded_flags) - expanded_flags.gsub(debug_option_pattern, '\\1-ggdb3\\2') - else - flags + " -ggdb3" - end - else - flags - end -end - -checking_for(checking_message("--enable-debug-build option")) do - enable_debug_build = enable_config("debug-build", false) - if enable_debug_build - $CFLAGS = disable_optimization_build_flag($CFLAGS) - $CFLAGS = enable_debug_build_flag($CFLAGS) - end - enable_debug_build -end - -libffi_version = nil -have_libffi = false -bundle = with_config("libffi-source-dir") -unless bundle - dir_config 'libffi' - - if pkg_config("libffi") - libffi_version = pkg_config("libffi", "modversion") - end - - have_ffi_header = false - if have_header(ffi_header = 'ffi.h') - have_ffi_header = true - elsif have_header(ffi_header = 'ffi/ffi.h') - $defs.push('-DUSE_HEADER_HACKS') - have_ffi_header = true - end - if have_ffi_header && (have_library('ffi') || have_library('libffi')) - have_libffi = true - checking_for("undefined FFI_GO_CLOSURES is used") do - if egrep_cpp(/warning: 'FFI_GO_CLOSURES' is not defined/, cpp_include(ffi_header), "2>&1") - $defs.push('-DFFI_GO_CLOSURES=0') - end - end - end -end - -unless have_libffi - if bundle - libffi_srcdir = libffi_package_name = bundle - else - raise "missing libffi. Please install libffi or use --with-libffi-source-dir with libffi source location." - end - ffi_header = 'ffi.h' - libffi = Struct.new(*%I[dir srcdir builddir include lib a cflags ldflags opt arch]).new - libffi.dir = libffi_package_name - if $srcdir == "." - libffi.builddir = libffi_package_name - libffi.srcdir = "." - else - libffi.builddir = libffi.dir - libffi.srcdir = relative_from(libffi_srcdir, "..") - end - libffi.include = "#{libffi.builddir}/include" - libffi.lib = "#{libffi.builddir}/.libs" - libffi.a = "#{libffi.lib}/libffi_convenience.#{$LIBEXT}" - nowarn = CONFIG.merge("warnflags"=>"") - libffi.cflags = RbConfig.expand("$(CFLAGS)".dup, nowarn) - libffi_version = libffi_package_name[/libffi-(.*)/, 1] - - FileUtils.mkdir_p(libffi.dir) - libffi.opt = CONFIG['configure_args'][/'(-C)'/, 1] - libffi.ldflags = RbConfig.expand("$(LDFLAGS) #{libpathflag([relative_from($topdir, "..")])} #{$LIBRUBYARG}".dup) - libffi.arch = RbConfig::CONFIG['host'] - if $mswin - unless find_executable(as = /x64/ =~ libffi.arch ? "ml64" : "ml") - raise "missing #{as} command." - end - $defs << "-DFFI_BUILDING" - libffi_config = "#{relative_from($srcdir, '..')}/win32/libffi-config.rb" - config = CONFIG.merge("top_srcdir" => $top_srcdir) - args = $ruby.gsub(/:\/=\\/, '') - args.gsub!(/\)\\/, ')/') - args = args.shellsplit - args.map! {|s| RbConfig.expand(s, config)} - args << '-C' << libffi.dir << libffi_config - opts = {} - else - args = %W[sh #{libffi.srcdir}/configure ] - opts = {chdir: libffi.dir} - end - cc = RbConfig::CONFIG['CC'] - cxx = RbConfig::CONFIG['CXX'] - ld = RbConfig::CONFIG['LD'] - args.concat %W[ - --srcdir=#{libffi.srcdir} - --host=#{libffi.arch} - ] - args << ($enable_shared || !$static ? '--enable-shared' : '--enable-static') - args << libffi.opt if libffi.opt - args.concat %W[ - CC=#{cc} CFLAGS=#{libffi.cflags} - CXX=#{cxx} CXXFLAGS=#{RbConfig.expand("$(CXXFLAGS)".dup, nowarn)} - LD=#{ld} LDFLAGS=#{libffi.ldflags} - ] - - FileUtils.rm_f("#{libffi.include}/ffitarget.h") - Logging::open do - Logging.message("%p in %p\n", args, opts) - unless system(*args, **opts) - begin - IO.copy_stream(libffi.dir + "/config.log", Logging.instance_variable_get(:@logfile)) - rescue SystemCallError => e - Logging.message("%s\n", e.message) - end - raise "failed to configure libffi. Please install libffi." - end - end - if $mswin && File.file?("#{libffi.include}/ffitarget.h") - FileUtils.rm_f("#{libffi.include}/ffitarget.h") - end - unless File.file?("#{libffi.include}/ffitarget.h") - FileUtils.cp("#{libffi_srcdir}/src/x86/ffitarget.h", libffi.include, preserve: true) - end - $INCFLAGS << " -I" << libffi.include -end - -if libffi_version - # If libffi_version contains rc version, just ignored. - libffi_version = libffi_version.gsub(/-rc\d+/, '') - libffi_version = (libffi_version.split('.').map(&:to_i) + [0,0])[0,3] - $defs.push(%{-DRUBY_LIBFFI_MODVERSION=#{ '%d%03d%03d' % libffi_version }}) - warn "libffi_version: #{libffi_version.join('.')}" -end - -case -when $mswin, $mingw, (libffi_version && (libffi_version <=> [3, 2]) >= 0) - $defs << "-DUSE_FFI_CLOSURE_ALLOC=1" -when (libffi_version && (libffi_version <=> [3, 2]) < 0) -else - have_func('ffi_closure_alloc', ffi_header) -end - -if libffi_version - if (libffi_version <=> [3, 0, 11]) >= 0 - $defs << "-DHAVE_FFI_PREP_CIF_VAR" - end -else - have_func('ffi_prep_cif_var', ffi_header) -end - -have_header 'sys/mman.h' -have_header 'link.h' - -if have_header "dlfcn.h" - have_library "dl" - - %w{ dlopen dlclose dlsym }.each do |func| - abort "missing function #{func}" unless have_func(func) - end - - have_func "dlerror" - have_func "dlinfo" - have_const("RTLD_DI_LINKMAP", "dlfcn.h") -elsif have_header "windows.h" - %w{ LoadLibrary FreeLibrary GetProcAddress GetModuleFileName }.each do |func| - abort "missing function #{func}" unless have_func(func) - end - - have_library "ws2_32" -end - -have_const('FFI_STDCALL', ffi_header) - -config = File.read(RbConfig.expand(File.join($arch_hdrdir, "ruby/config.h"))) -types = {"SIZE_T"=>"SSIZE_T", "PTRDIFF_T"=>nil, "INTPTR_T"=>nil} -types.each do |type, signed| - if /^\#define\s+SIZEOF_#{type}\s+(SIZEOF_(.+)|\d+)/ =~ config - if size = $2 and size != 'VOIDP' - size = types.fetch(size) {size} - $defs << "-DTYPE_#{signed||type}=TYPE_#{size}" - end - if signed - check_signedness(type.downcase, "stddef.h") - end - else - check_signedness(type.downcase, "stddef.h") - end -end - -if libffi - $LOCAL_LIBS.prepend("#{libffi.a} ").strip! # to exts.mk - $INCFLAGS.gsub!(/-I#{libffi.dir}/, '-I$(LIBFFI_DIR)') -end -create_makefile 'fiddle' do |conf| - if !libffi - next conf << "LIBFFI_CLEAN = none\n" - elsif $gnumake && !$nmake - submake_arg = "-C $(LIBFFI_DIR)\n" - else - submake_pre = "cd $(LIBFFI_DIR) && #{config_string("exec")}".strip - end - if $nmake - cmd = "$(RUBY) -C $(LIBFFI_DIR) #{libffi_config} --srcdir=$(LIBFFI_SRCDIR)" - else - cmd = "cd $(LIBFFI_DIR) && #$exec $(LIBFFI_SRCDIR)/configure #{libffi.opt}" - end - sep = "/" - seprpl = config_string('BUILD_FILE_SEPARATOR') {|s| sep = s; ":/=#{s}" if s != "/"} || "" - conf << <<-MK.gsub(/^ +| +$/, '') - PWD = - LIBFFI_CONFIGURE = #{cmd} - LIBFFI_ARCH = #{libffi.arch} - LIBFFI_SRCDIR = #{libffi.srcdir.sub(libffi.dir, '$(LIBFFI_DIR)')} - LIBFFI_DIR = #{libffi.dir} - LIBFFI_A = #{libffi.a.sub(libffi.dir, '$(LIBFFI_DIR)')} - LIBFFI_CFLAGS = #{libffi.cflags} - LIBFFI_LDFLAGS = #{libffi.ldflags} - FFI_H = $(LIBFFI_DIR)/include/ffi.h - SUBMAKE_PRE = #{submake_pre} - SUBMAKE_ARG = #{submake_arg} - LIBFFI_CLEAN = libffi - MK -end - -if libffi - $LIBPATH.pop -end - -# :startdoc: diff --git a/ext/fiddle/fiddle.c b/ext/fiddle/fiddle.c deleted file mode 100644 index f420d9fa3b..0000000000 --- a/ext/fiddle/fiddle.c +++ /dev/null @@ -1,712 +0,0 @@ -#include <stdbool.h> - -#include <fiddle.h> - -VALUE mFiddle; -VALUE rb_eFiddleDLError; -VALUE rb_eFiddleError; - -void Init_fiddle_pointer(void); -void Init_fiddle_pinned(void); - -#ifdef HAVE_RUBY_MEMORY_VIEW_H -void Init_fiddle_memory_view(void); -#endif - -/* - * call-seq: Fiddle.malloc(size) - * - * Allocate +size+ bytes of memory and return the integer memory address - * for the allocated memory. - */ -static VALUE -rb_fiddle_malloc(VALUE self, VALUE size) -{ - void *ptr; - ptr = (void*)ruby_xcalloc(1, NUM2SIZET(size)); - return PTR2NUM(ptr); -} - -/* - * call-seq: Fiddle.realloc(addr, size) - * - * Change the size of the memory allocated at the memory location +addr+ to - * +size+ bytes. Returns the memory address of the reallocated memory, which - * may be different than the address passed in. - */ -static VALUE -rb_fiddle_realloc(VALUE self, VALUE addr, VALUE size) -{ - void *ptr = NUM2PTR(addr); - - ptr = (void*)ruby_xrealloc(ptr, NUM2SIZET(size)); - return PTR2NUM(ptr); -} - -/* - * call-seq: Fiddle.free(addr) - * - * Free the memory at address +addr+ - */ -VALUE -rb_fiddle_free(VALUE self, VALUE addr) -{ - void *ptr = NUM2PTR(addr); - - ruby_xfree(ptr); - return Qnil; -} - -/* - * call-seq: Fiddle.dlunwrap(addr) - * - * Returns the Ruby object stored at the memory address +addr+ - * - * Example: - * - * x = Object.new - * # => #<Object:0x0000000107c7d870> - * Fiddle.dlwrap(x) - * # => 4425504880 - * Fiddle.dlunwrap(_) - * # => #<Object:0x0000000107c7d870> - */ -VALUE -rb_fiddle_ptr2value(VALUE self, VALUE addr) -{ - return (VALUE)NUM2PTR(addr); -} - -/* - * call-seq: Fiddle.dlwrap(val) - * - * Returns the memory address of the Ruby object stored at +val+ - * - * Example: - * - * x = Object.new - * # => #<Object:0x0000000107c7d870> - * Fiddle.dlwrap(x) - * # => 4425504880 - * - * In the case +val+ is not a heap allocated object, this method will return - * the tagged pointer value. - * - * Example: - * - * Fiddle.dlwrap(123) - * # => 247 - */ -static VALUE -rb_fiddle_value2ptr(VALUE self, VALUE val) -{ - return PTR2NUM((void*)val); -} - -void Init_fiddle_handle(void); - -void -Init_fiddle(void) -{ - /* - * Document-module: Fiddle - * - * A libffi wrapper for Ruby. - * - * == Description - * - * Fiddle is an extension to translate a foreign function interface (FFI) - * with ruby. - * - * It wraps {libffi}[http://sourceware.org/libffi/], a popular C library - * which provides a portable interface that allows code written in one - * language to call code written in another language. - * - * == Example - * - * Here we will use Fiddle::Function to wrap {floor(3) from - * libm}[http://linux.die.net/man/3/floor] - * - * require 'fiddle' - * - * libm = Fiddle.dlopen('/lib/libm.so.6') - * - * floor = Fiddle::Function.new( - * libm['floor'], - * [Fiddle::TYPE_DOUBLE], - * Fiddle::TYPE_DOUBLE - * ) - * - * puts floor.call(3.14159) #=> 3.0 - * - * - */ - mFiddle = rb_define_module("Fiddle"); - - /* - * Document-class: Fiddle::Error - * - * Generic error class for Fiddle - */ - rb_eFiddleError = rb_define_class_under(mFiddle, "Error", rb_eStandardError); - - /* - * Ruby installed by RubyInstaller for Windows always require - * bundled Fiddle because ruby_installer/runtime/dll_directory.rb - * requires Fiddle. It's used by - * rubygems/defaults/operating_system.rb. It means that the - * bundled Fiddle is always required on initialization. - * - * We just remove existing Fiddle::DLError here to override - * the bundled Fiddle. - */ - if (rb_const_defined(mFiddle, rb_intern("DLError"))) { - rb_const_remove(mFiddle, rb_intern("DLError")); - } - - /* - * Document-class: Fiddle::DLError - * - * standard dynamic load exception - */ - rb_eFiddleDLError = rb_define_class_under(mFiddle, "DLError", rb_eFiddleError); - - VALUE mFiddleTypes = rb_define_module_under(mFiddle, "Types"); - - /* Document-const: Fiddle::Types::VOID - * - * C type - void - */ - rb_define_const(mFiddleTypes, "VOID", INT2NUM(TYPE_VOID)); - - /* Document-const: Fiddle::Types::VOIDP - * - * C type - void* - */ - rb_define_const(mFiddleTypes, "VOIDP", INT2NUM(TYPE_VOIDP)); - - /* Document-const: Fiddle::Types::CHAR - * - * C type - char - */ - rb_define_const(mFiddleTypes, "CHAR", INT2NUM(TYPE_CHAR)); - - /* Document-const: Fiddle::Types::UCHAR - * - * C type - unsigned char - */ - rb_define_const(mFiddleTypes, "UCHAR", INT2NUM(TYPE_UCHAR)); - - /* Document-const: Fiddle::Types::SHORT - * - * C type - short - */ - rb_define_const(mFiddleTypes, "SHORT", INT2NUM(TYPE_SHORT)); - - /* Document-const: Fiddle::Types::USHORT - * - * C type - unsigned short - */ - rb_define_const(mFiddleTypes, "USHORT", INT2NUM(TYPE_USHORT)); - - /* Document-const: Fiddle::Types::INT - * - * C type - int - */ - rb_define_const(mFiddleTypes, "INT", INT2NUM(TYPE_INT)); - - /* Document-const: Fiddle::Types::UINT - * - * C type - unsigned int - */ - rb_define_const(mFiddleTypes, "UINT", INT2NUM(TYPE_UINT)); - - /* Document-const: Fiddle::Types::LONG - * - * C type - long - */ - rb_define_const(mFiddleTypes, "LONG", INT2NUM(TYPE_LONG)); - - /* Document-const: Fiddle::Types::ULONG - * - * C type - long - */ - rb_define_const(mFiddleTypes, "ULONG", INT2NUM(TYPE_ULONG)); - -#if HAVE_LONG_LONG - /* Document-const: Fiddle::Types::LONG_LONG - * - * C type - long long - */ - rb_define_const(mFiddleTypes, "LONG_LONG", INT2NUM(TYPE_LONG_LONG)); - - /* Document-const: Fiddle::Types::ULONG_LONG - * - * C type - long long - */ - rb_define_const(mFiddleTypes, "ULONG_LONG", INT2NUM(TYPE_ULONG_LONG)); -#endif - -#ifdef TYPE_INT8_T - /* Document-const: Fiddle::Types::INT8_T - * - * C type - int8_t - */ - rb_define_const(mFiddleTypes, "INT8_T", INT2NUM(TYPE_INT8_T)); - - /* Document-const: Fiddle::Types::UINT8_T - * - * C type - uint8_t - */ - rb_define_const(mFiddleTypes, "UINT8_T", INT2NUM(TYPE_UINT8_T)); -#endif - -#ifdef TYPE_INT16_T - /* Document-const: Fiddle::Types::INT16_T - * - * C type - int16_t - */ - rb_define_const(mFiddleTypes, "INT16_T", INT2NUM(TYPE_INT16_T)); - - /* Document-const: Fiddle::Types::UINT16_T - * - * C type - uint16_t - */ - rb_define_const(mFiddleTypes, "UINT16_T", INT2NUM(TYPE_UINT16_T)); -#endif - -#ifdef TYPE_INT32_T - /* Document-const: Fiddle::Types::INT32_T - * - * C type - int32_t - */ - rb_define_const(mFiddleTypes, "INT32_T", INT2NUM(TYPE_INT32_T)); - - /* Document-const: Fiddle::Types::UINT32_T - * - * C type - uint32_t - */ - rb_define_const(mFiddleTypes, "UINT32_T", INT2NUM(TYPE_UINT32_T)); -#endif - -#ifdef TYPE_INT64_T - /* Document-const: Fiddle::Types::INT64_T - * - * C type - int64_t - */ - rb_define_const(mFiddleTypes, "INT64_T", INT2NUM(TYPE_INT64_T)); - - /* Document-const: Fiddle::Types::UINT64_T - * - * C type - uint64_t - */ - rb_define_const(mFiddleTypes, "UINT64_T", INT2NUM(TYPE_UINT64_T)); -#endif - - /* Document-const: Fiddle::Types::FLOAT - * - * C type - float - */ - rb_define_const(mFiddleTypes, "FLOAT", INT2NUM(TYPE_FLOAT)); - - /* Document-const: Fiddle::Types::DOUBLE - * - * C type - double - */ - rb_define_const(mFiddleTypes, "DOUBLE", INT2NUM(TYPE_DOUBLE)); - -#ifdef HAVE_FFI_PREP_CIF_VAR - /* Document-const: Fiddle::Types::VARIADIC - * - * C type - ... - */ - rb_define_const(mFiddleTypes, "VARIADIC", INT2NUM(TYPE_VARIADIC)); -#endif - - /* Document-const: Fiddle::Types::CONST_STRING - * - * C type - const char* ('\0' terminated const char*) - */ - rb_define_const(mFiddleTypes, "CONST_STRING", INT2NUM(TYPE_CONST_STRING)); - - /* Document-const: Fiddle::Types::SIZE_T - * - * C type - size_t - */ - rb_define_const(mFiddleTypes, "SIZE_T", INT2NUM(TYPE_SIZE_T)); - - /* Document-const: Fiddle::Types::SSIZE_T - * - * C type - ssize_t - */ - rb_define_const(mFiddleTypes, "SSIZE_T", INT2NUM(TYPE_SSIZE_T)); - - /* Document-const: Fiddle::Types::PTRDIFF_T - * - * C type - ptrdiff_t - */ - rb_define_const(mFiddleTypes, "PTRDIFF_T", INT2NUM(TYPE_PTRDIFF_T)); - - /* Document-const: Fiddle::Types::INTPTR_T - * - * C type - intptr_t - */ - rb_define_const(mFiddleTypes, "INTPTR_T", INT2NUM(TYPE_INTPTR_T)); - - /* Document-const: Fiddle::Types::UINTPTR_T - * - * C type - uintptr_t - */ - rb_define_const(mFiddleTypes, "UINTPTR_T", INT2NUM(TYPE_UINTPTR_T)); - - /* Document-const: Fiddle::Types::BOOL - * - * C type - bool - */ - rb_define_const(mFiddleTypes, "BOOL" , INT2NUM(TYPE_BOOL)); - - /* Document-const: ALIGN_VOIDP - * - * The alignment size of a void* - */ - rb_define_const(mFiddle, "ALIGN_VOIDP", INT2NUM(ALIGN_VOIDP)); - - /* Document-const: ALIGN_CHAR - * - * The alignment size of a char - */ - rb_define_const(mFiddle, "ALIGN_CHAR", INT2NUM(ALIGN_CHAR)); - - /* Document-const: ALIGN_SHORT - * - * The alignment size of a short - */ - rb_define_const(mFiddle, "ALIGN_SHORT", INT2NUM(ALIGN_SHORT)); - - /* Document-const: ALIGN_INT - * - * The alignment size of an int - */ - rb_define_const(mFiddle, "ALIGN_INT", INT2NUM(ALIGN_INT)); - - /* Document-const: ALIGN_LONG - * - * The alignment size of a long - */ - rb_define_const(mFiddle, "ALIGN_LONG", INT2NUM(ALIGN_LONG)); - -#if HAVE_LONG_LONG - /* Document-const: ALIGN_LONG_LONG - * - * The alignment size of a long long - */ - rb_define_const(mFiddle, "ALIGN_LONG_LONG", INT2NUM(ALIGN_LONG_LONG)); -#endif - - /* Document-const: ALIGN_INT8_T - * - * The alignment size of a int8_t - */ - rb_define_const(mFiddle, "ALIGN_INT8_T", INT2NUM(ALIGN_INT8_T)); - - /* Document-const: ALIGN_INT16_T - * - * The alignment size of a int16_t - */ - rb_define_const(mFiddle, "ALIGN_INT16_T", INT2NUM(ALIGN_INT16_T)); - - /* Document-const: ALIGN_INT32_T - * - * The alignment size of a int32_t - */ - rb_define_const(mFiddle, "ALIGN_INT32_T", INT2NUM(ALIGN_INT32_T)); - - /* Document-const: ALIGN_INT64_T - * - * The alignment size of a int64_t - */ - rb_define_const(mFiddle, "ALIGN_INT64_T", INT2NUM(ALIGN_INT64_T)); - - /* Document-const: ALIGN_FLOAT - * - * The alignment size of a float - */ - rb_define_const(mFiddle, "ALIGN_FLOAT", INT2NUM(ALIGN_FLOAT)); - - /* Document-const: ALIGN_DOUBLE - * - * The alignment size of a double - */ - rb_define_const(mFiddle, "ALIGN_DOUBLE",INT2NUM(ALIGN_DOUBLE)); - - /* Document-const: ALIGN_SIZE_T - * - * The alignment size of a size_t - */ - rb_define_const(mFiddle, "ALIGN_SIZE_T", INT2NUM(ALIGN_OF(size_t))); - - /* Document-const: ALIGN_SSIZE_T - * - * The alignment size of a ssize_t - */ - rb_define_const(mFiddle, "ALIGN_SSIZE_T", INT2NUM(ALIGN_OF(size_t))); /* same as size_t */ - - /* Document-const: ALIGN_PTRDIFF_T - * - * The alignment size of a ptrdiff_t - */ - rb_define_const(mFiddle, "ALIGN_PTRDIFF_T", INT2NUM(ALIGN_OF(ptrdiff_t))); - - /* Document-const: ALIGN_INTPTR_T - * - * The alignment size of a intptr_t - */ - rb_define_const(mFiddle, "ALIGN_INTPTR_T", INT2NUM(ALIGN_OF(intptr_t))); - - /* Document-const: ALIGN_UINTPTR_T - * - * The alignment size of a uintptr_t - */ - rb_define_const(mFiddle, "ALIGN_UINTPTR_T", INT2NUM(ALIGN_OF(uintptr_t))); - - /* Document-const: ALIGN_BOOL - * - * The alignment size of a bool - */ - rb_define_const(mFiddle, "ALIGN_BOOL", INT2NUM(ALIGN_OF(bool))); - - /* Document-const: WINDOWS - * - * Returns a boolean regarding whether the host is WIN32 - */ -#if defined(_WIN32) - rb_define_const(mFiddle, "WINDOWS", Qtrue); -#else - rb_define_const(mFiddle, "WINDOWS", Qfalse); -#endif - - /* Document-const: SIZEOF_VOIDP - * - * size of a void* - */ - rb_define_const(mFiddle, "SIZEOF_VOIDP", INT2NUM(sizeof(void*))); - - /* Document-const: SIZEOF_CHAR - * - * size of a char - */ - rb_define_const(mFiddle, "SIZEOF_CHAR", INT2NUM(sizeof(char))); - - /* Document-const: SIZEOF_UCHAR - * - * size of a unsigned char - */ - rb_define_const(mFiddle, "SIZEOF_UCHAR", INT2NUM(sizeof(unsigned char))); - - /* Document-const: SIZEOF_SHORT - * - * size of a short - */ - rb_define_const(mFiddle, "SIZEOF_SHORT", INT2NUM(sizeof(short))); - - /* Document-const: SIZEOF_USHORT - * - * size of a unsigned short - */ - rb_define_const(mFiddle, "SIZEOF_USHORT", INT2NUM(sizeof(unsigned short))); - - /* Document-const: SIZEOF_INT - * - * size of an int - */ - rb_define_const(mFiddle, "SIZEOF_INT", INT2NUM(sizeof(int))); - - /* Document-const: SIZEOF_UINT - * - * size of an unsigned int - */ - rb_define_const(mFiddle, "SIZEOF_UINT", INT2NUM(sizeof(unsigned int))); - - /* Document-const: SIZEOF_LONG - * - * size of a long - */ - rb_define_const(mFiddle, "SIZEOF_LONG", INT2NUM(sizeof(long))); - - /* Document-const: SIZEOF_ULONG - * - * size of a unsigned long - */ - rb_define_const(mFiddle, "SIZEOF_ULONG", INT2NUM(sizeof(unsigned long))); - -#if HAVE_LONG_LONG - /* Document-const: SIZEOF_LONG_LONG - * - * size of a long long - */ - rb_define_const(mFiddle, "SIZEOF_LONG_LONG", INT2NUM(sizeof(LONG_LONG))); - - /* Document-const: SIZEOF_ULONG_LONG - * - * size of a unsigned long long - */ - rb_define_const(mFiddle, "SIZEOF_ULONG_LONG", INT2NUM(sizeof(unsigned LONG_LONG))); -#endif - - /* Document-const: SIZEOF_INT8_T - * - * size of a int8_t - */ - rb_define_const(mFiddle, "SIZEOF_INT8_T", INT2NUM(sizeof(int8_t))); - - /* Document-const: SIZEOF_UINT8_T - * - * size of a uint8_t - */ - rb_define_const(mFiddle, "SIZEOF_UINT8_T", INT2NUM(sizeof(uint8_t))); - - /* Document-const: SIZEOF_INT16_T - * - * size of a int16_t - */ - rb_define_const(mFiddle, "SIZEOF_INT16_T", INT2NUM(sizeof(int16_t))); - - /* Document-const: SIZEOF_UINT16_T - * - * size of a uint16_t - */ - rb_define_const(mFiddle, "SIZEOF_UINT16_T", INT2NUM(sizeof(uint16_t))); - - /* Document-const: SIZEOF_INT32_T - * - * size of a int32_t - */ - rb_define_const(mFiddle, "SIZEOF_INT32_T", INT2NUM(sizeof(int32_t))); - - /* Document-const: SIZEOF_UINT32_T - * - * size of a uint32_t - */ - rb_define_const(mFiddle, "SIZEOF_UINT32_T", INT2NUM(sizeof(uint32_t))); - - /* Document-const: SIZEOF_INT64_T - * - * size of a int64_t - */ - rb_define_const(mFiddle, "SIZEOF_INT64_T", INT2NUM(sizeof(int64_t))); - - /* Document-const: SIZEOF_UINT64_T - * - * size of a uint64_t - */ - rb_define_const(mFiddle, "SIZEOF_UINT64_T", INT2NUM(sizeof(uint64_t))); - - /* Document-const: SIZEOF_FLOAT - * - * size of a float - */ - rb_define_const(mFiddle, "SIZEOF_FLOAT", INT2NUM(sizeof(float))); - - /* Document-const: SIZEOF_DOUBLE - * - * size of a double - */ - rb_define_const(mFiddle, "SIZEOF_DOUBLE",INT2NUM(sizeof(double))); - - /* Document-const: SIZEOF_SIZE_T - * - * size of a size_t - */ - rb_define_const(mFiddle, "SIZEOF_SIZE_T", INT2NUM(sizeof(size_t))); - - /* Document-const: SIZEOF_SSIZE_T - * - * size of a ssize_t - */ - rb_define_const(mFiddle, "SIZEOF_SSIZE_T", INT2NUM(sizeof(size_t))); /* same as size_t */ - - /* Document-const: SIZEOF_PTRDIFF_T - * - * size of a ptrdiff_t - */ - rb_define_const(mFiddle, "SIZEOF_PTRDIFF_T", INT2NUM(sizeof(ptrdiff_t))); - - /* Document-const: SIZEOF_INTPTR_T - * - * size of a intptr_t - */ - rb_define_const(mFiddle, "SIZEOF_INTPTR_T", INT2NUM(sizeof(intptr_t))); - - /* Document-const: SIZEOF_UINTPTR_T - * - * size of a uintptr_t - */ - rb_define_const(mFiddle, "SIZEOF_UINTPTR_T", INT2NUM(sizeof(uintptr_t))); - - /* Document-const: SIZEOF_CONST_STRING - * - * size of a const char* - */ - rb_define_const(mFiddle, "SIZEOF_CONST_STRING", INT2NUM(sizeof(const char*))); - - /* Document-const: SIZEOF_BOOL - * - * size of a bool - */ - rb_define_const(mFiddle, "SIZEOF_BOOL", INT2NUM(sizeof(bool))); - - /* Document-const: RUBY_FREE - * - * Address of the ruby_xfree() function - */ - rb_define_const(mFiddle, "RUBY_FREE", PTR2NUM(ruby_xfree)); - - /* Document-const: BUILD_RUBY_PLATFORM - * - * Platform built against (i.e. "x86_64-linux", etc.) - * - * See also RUBY_PLATFORM - */ - rb_define_const(mFiddle, "BUILD_RUBY_PLATFORM", rb_str_new2(RUBY_PLATFORM)); - - rb_define_module_function(mFiddle, "dlwrap", rb_fiddle_value2ptr, 1); - rb_define_module_function(mFiddle, "dlunwrap", rb_fiddle_ptr2value, 1); - rb_define_module_function(mFiddle, "malloc", rb_fiddle_malloc, 1); - rb_define_module_function(mFiddle, "realloc", rb_fiddle_realloc, 2); - rb_define_module_function(mFiddle, "free", rb_fiddle_free, 1); - - /* Document-const: Qtrue - * - * The value of Qtrue - */ - rb_define_const(mFiddle, "Qtrue", INT2NUM(Qtrue)); - - /* Document-const: Qfalse - * - * The value of Qfalse - */ - rb_define_const(mFiddle, "Qfalse", INT2NUM(Qfalse)); - - /* Document-const: Qnil - * - * The value of Qnil - */ - rb_define_const(mFiddle, "Qnil", INT2NUM(Qnil)); - - /* Document-const: Qundef - * - * The value of Qundef - */ - rb_define_const(mFiddle, "Qundef", INT2NUM(Qundef)); - - Init_fiddle_function(); - Init_fiddle_closure(); - Init_fiddle_handle(); - Init_fiddle_pointer(); - Init_fiddle_pinned(); - -#ifdef HAVE_RUBY_MEMORY_VIEW_H - Init_fiddle_memory_view(); -#endif -} -/* vim: set noet sws=4 sw=4: */ diff --git a/ext/fiddle/fiddle.gemspec b/ext/fiddle/fiddle.gemspec deleted file mode 100644 index 878109395b..0000000000 --- a/ext/fiddle/fiddle.gemspec +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -version_module = Module.new do - version_rb = File.join(__dir__, "lib/fiddle/version.rb") - module_eval(File.read(version_rb), version_rb, __LINE__) -end - -Gem::Specification.new do |spec| - spec.name = "fiddle" - spec.version = version_module::Fiddle::VERSION - spec.authors = ["Aaron Patterson", "SHIBATA Hiroshi"] - spec.email = ["aaron@tenderlovemaking.com", "hsbt@ruby-lang.org"] - - spec.summary = %q{A libffi wrapper for Ruby.} - spec.description = %q{A libffi wrapper for Ruby.} - spec.homepage = "https://github.com/ruby/fiddle" - spec.licenses = ["Ruby", "BSD-2-Clause"] - - spec.files = [ - "LICENSE.txt", - "README.md", - "Rakefile", - "ext/fiddle/closure.c", - "ext/fiddle/closure.h", - "ext/fiddle/conversions.c", - "ext/fiddle/conversions.h", - "ext/fiddle/depend", - "ext/fiddle/extconf.rb", - "ext/fiddle/fiddle.c", - "ext/fiddle/fiddle.h", - "ext/fiddle/function.c", - "ext/fiddle/function.h", - "ext/fiddle/handle.c", - "ext/fiddle/memory_view.c", - "ext/fiddle/pinned.c", - "ext/fiddle/pointer.c", - "ext/fiddle/win32/fficonfig.h", - "ext/fiddle/win32/libffi-3.2.1-mswin.patch", - "ext/fiddle/win32/libffi-config.rb", - "ext/fiddle/win32/libffi.mk.tmpl", - "fiddle.gemspec", - "lib/fiddle.rb", - "lib/fiddle/closure.rb", - "lib/fiddle/cparser.rb", - "lib/fiddle/function.rb", - "lib/fiddle/import.rb", - "lib/fiddle/pack.rb", - "lib/fiddle/struct.rb", - "lib/fiddle/types.rb", - "lib/fiddle/value.rb", - "lib/fiddle/version.rb", - ] - spec.require_paths = ["lib"] - spec.extensions = ["ext/fiddle/extconf.rb"] - - spec.required_ruby_version = ">= 2.5.0" - - spec.metadata["msys2_mingw_dependencies"] = "libffi" -end diff --git a/ext/fiddle/fiddle.h b/ext/fiddle/fiddle.h deleted file mode 100644 index 348baa9ab9..0000000000 --- a/ext/fiddle/fiddle.h +++ /dev/null @@ -1,240 +0,0 @@ -#ifndef FIDDLE_H -#define FIDDLE_H - -#include <ruby.h> -#include <errno.h> - -#if defined(_WIN32) -#include <windows.h> -#endif - -#ifdef HAVE_SYS_MMAN_H -#include <sys/mman.h> -#endif - -#if defined(HAVE_LINK_H) -# include <link.h> -#endif - -#if defined(HAVE_DLFCN_H) -# include <dlfcn.h> -# /* some stranger systems may not define all of these */ -#ifndef RTLD_LAZY -#define RTLD_LAZY 0 -#endif -#ifndef RTLD_GLOBAL -#define RTLD_GLOBAL 0 -#endif -#ifndef RTLD_NOW -#define RTLD_NOW 0 -#endif -#else -# if defined(_WIN32) -# include <windows.h> -# define dlopen(name,flag) ((void*)LoadLibrary(name)) -# define dlerror() strerror(rb_w32_map_errno(GetLastError())) -# define dlsym(handle,name) ((void*)GetProcAddress((handle),(name))) -# define RTLD_LAZY -1 -# define RTLD_NOW -1 -# define RTLD_GLOBAL -1 -# endif -#endif - -#ifdef USE_HEADER_HACKS -#include <ffi/ffi.h> -#else -#include <ffi.h> -#endif - -#undef ffi_type_uchar -#undef ffi_type_schar -#undef ffi_type_ushort -#undef ffi_type_sshort -#undef ffi_type_uint -#undef ffi_type_sint -#undef ffi_type_ulong -#undef ffi_type_slong - -#if CHAR_BIT == 8 -# define ffi_type_uchar ffi_type_uint8 -# define ffi_type_schar ffi_type_sint8 -#else -# error "CHAR_BIT not supported" -#endif - -#if SIZEOF_SHORT == 2 -# define ffi_type_ushort ffi_type_uint16 -# define ffi_type_sshort ffi_type_sint16 -#elif SIZEOF_SHORT == 4 -# define ffi_type_ushort ffi_type_uint32 -# define ffi_type_sshort ffi_type_sint32 -#else -# error "short size not supported" -#endif - -#if SIZEOF_INT == 2 -# define ffi_type_uint ffi_type_uint16 -# define ffi_type_sint ffi_type_sint16 -#elif SIZEOF_INT == 4 -# define ffi_type_uint ffi_type_uint32 -# define ffi_type_sint ffi_type_sint32 -#elif SIZEOF_INT == 8 -# define ffi_type_uint ffi_type_uint64 -# define ffi_type_sint ffi_type_sint64 -#else -# error "int size not supported" -#endif - -#if SIZEOF_LONG == 4 -# define ffi_type_ulong ffi_type_uint32 -# define ffi_type_slong ffi_type_sint32 -#elif SIZEOF_LONG == 8 -# define ffi_type_ulong ffi_type_uint64 -# define ffi_type_slong ffi_type_sint64 -#else -# error "long size not supported" -#endif - -#if HAVE_LONG_LONG -# if SIZEOF_LONG_LONG == 8 -# define ffi_type_slong_long ffi_type_sint64 -# define ffi_type_ulong_long ffi_type_uint64 -# else -# error "long long size not supported" -# endif -#endif - -#include <closure.h> -#include <conversions.h> -#include <function.h> - -#define TYPE_VOID 0 -#define TYPE_VOIDP 1 -#define TYPE_CHAR 2 -#define TYPE_UCHAR -TYPE_CHAR -#define TYPE_SHORT 3 -#define TYPE_USHORT -TYPE_SHORT -#define TYPE_INT 4 -#define TYPE_UINT -TYPE_INT -#define TYPE_LONG 5 -#define TYPE_ULONG -TYPE_LONG -#ifdef HAVE_LONG_LONG -#define TYPE_LONG_LONG 6 -#define TYPE_ULONG_LONG -TYPE_LONG_LONG -#endif -#define TYPE_FLOAT 7 -#define TYPE_DOUBLE 8 -#define TYPE_VARIADIC 9 -#define TYPE_CONST_STRING 10 -#define TYPE_BOOL 11 - -#define TYPE_INT8_T TYPE_CHAR -#define TYPE_UINT8_T -TYPE_INT8_T - -#if SIZEOF_SHORT == 2 -# define TYPE_INT16_T TYPE_SHORT -#elif SIZEOF_INT == 2 -# define TYPE_INT16_T TYPE_INT -#endif - -#ifdef TYPE_INT16_T -# define TYPE_UINT16_T -TYPE_INT16_T -#endif - -#if SIZEOF_SHORT == 4 -# define TYPE_INT32_T TYPE_SHORT -#elif SIZEOF_INT == 4 -# define TYPE_INT32_T TYPE_INT -#elif SIZEOF_LONG == 4 -# define TYPE_INT32_T TYPE_LONG -#endif - -#ifdef TYPE_INT32_T -#define TYPE_UINT32_T -TYPE_INT32_T -#endif - -#if SIZEOF_INT == 8 -# define TYPE_INT64_T TYPE_INT -#elif SIZEOF_LONG == 8 -# define TYPE_INT64_T TYPE_LONG -#elif defined(TYPE_LONG_LONG) -# define TYPE_INT64_T TYPE_LONG_LONG -#endif - -#ifdef TYPE_INT64_T -#define TYPE_UINT64_T -TYPE_INT64_T -#endif - -#ifndef TYPE_SSIZE_T -# if SIZEOF_SIZE_T == SIZEOF_INT -# define TYPE_SSIZE_T TYPE_INT -# elif SIZEOF_SIZE_T == SIZEOF_LONG -# define TYPE_SSIZE_T TYPE_LONG -# elif defined HAVE_LONG_LONG && SIZEOF_SIZE_T == SIZEOF_LONG_LONG -# define TYPE_SSIZE_T TYPE_LONG_LONG -# endif -#endif -#define TYPE_SIZE_T (-1*SIGNEDNESS_OF_SIZE_T*TYPE_SSIZE_T) - -#ifndef TYPE_PTRDIFF_T -# if SIZEOF_PTRDIFF_T == SIZEOF_INT -# define TYPE_PTRDIFF_T TYPE_INT -# elif SIZEOF_PTRDIFF_T == SIZEOF_LONG -# define TYPE_PTRDIFF_T TYPE_LONG -# elif defined HAVE_LONG_LONG && SIZEOF_PTRDIFF_T == SIZEOF_LONG_LONG -# define TYPE_PTRDIFF_T TYPE_LONG_LONG -# endif -#endif - -#ifndef TYPE_INTPTR_T -# if SIZEOF_INTPTR_T == SIZEOF_INT -# define TYPE_INTPTR_T TYPE_INT -# elif SIZEOF_INTPTR_T == SIZEOF_LONG -# define TYPE_INTPTR_T TYPE_LONG -# elif defined HAVE_LONG_LONG && SIZEOF_INTPTR_T == SIZEOF_LONG_LONG -# define TYPE_INTPTR_T TYPE_LONG_LONG -# endif -#endif -#define TYPE_UINTPTR_T (-TYPE_INTPTR_T) - -/* GCC releases before GCC 4.9 had a bug in _Alignof. See GCC bug 52023 - <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52023>. - clang versions < 8.0.0 have the same bug. */ -#if defined(HAVE__ALIGNOF) -# /* Autoconf detected availability of a sane `_Alignof()`. */ -# define ALIGN_OF(type) RB_GNUC_EXTENSION(_Alignof(type)) -#elif (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112 \ - || (defined(__GNUC__) && __GNUC__ < 4 + (__GNUC_MINOR__ < 9) \ - && !defined(__clang__)) \ - || (defined(__clang__) && __clang_major__ < 8)) -# define ALIGN_OF(type) offsetof(struct {char align_c; type align_x;}, align_x) -#else -# define ALIGN_OF(type) _Alignof(type) -#endif - -#define ALIGN_VOIDP ALIGN_OF(void*) -#define ALIGN_CHAR ALIGN_OF(char) -#define ALIGN_SHORT ALIGN_OF(short) -#define ALIGN_INT ALIGN_OF(int) -#define ALIGN_LONG ALIGN_OF(long) -#if HAVE_LONG_LONG -#define ALIGN_LONG_LONG ALIGN_OF(LONG_LONG) -#endif -#define ALIGN_FLOAT ALIGN_OF(float) -#define ALIGN_DOUBLE ALIGN_OF(double) - -#define ALIGN_INT8_T ALIGN_OF(int8_t) -#define ALIGN_INT16_T ALIGN_OF(int16_t) -#define ALIGN_INT32_T ALIGN_OF(int32_t) -#define ALIGN_INT64_T ALIGN_OF(int64_t) - -extern VALUE mFiddle; -extern VALUE rb_eFiddleDLError; - -VALUE rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type); - -typedef void (*rb_fiddle_freefunc_t)(void*); -VALUE rb_fiddle_ptr_new_wrap(void *ptr, long size, rb_fiddle_freefunc_t func, VALUE wrap0, VALUE wrap1); - -#endif -/* vim: set noet sws=4 sw=4: */ diff --git a/ext/fiddle/function.c b/ext/fiddle/function.c deleted file mode 100644 index 5469e09e37..0000000000 --- a/ext/fiddle/function.c +++ /dev/null @@ -1,496 +0,0 @@ -#include <fiddle.h> -#include <ruby/thread.h> - -#include <stdbool.h> - -#ifdef PRIsVALUE -# define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj) -# define RB_OBJ_STRING(obj) (obj) -#else -# define PRIsVALUE "s" -# define RB_OBJ_CLASSNAME(obj) rb_obj_classname(obj) -# define RB_OBJ_STRING(obj) StringValueCStr(obj) -#endif - -VALUE cFiddleFunction; - -#define MAX_ARGS (SIZE_MAX / (sizeof(void *) + sizeof(fiddle_generic)) - 1) - -#define Check_Max_Args(name, len) \ - Check_Max_Args_(name, len, "") -#define Check_Max_Args_Long(name, len) \ - Check_Max_Args_(name, len, "l") -#define Check_Max_Args_(name, len, fmt) \ - do { \ - if ((size_t)(len) >= MAX_ARGS) { \ - rb_raise(rb_eTypeError, \ - "%s is so large " \ - "that it can cause integer overflow (%"fmt"d)", \ - (name), (len)); \ - } \ - } while (0) - -static void -deallocate(void *p) -{ - ffi_cif *cif = p; - if (cif->arg_types) xfree(cif->arg_types); - xfree(cif); -} - -static size_t -function_memsize(const void *p) -{ - /* const */ffi_cif *ptr = (ffi_cif *)p; - size_t size = 0; - - size += sizeof(*ptr); -#if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API - size += ffi_raw_size(ptr); -#endif - - return size; -} - -const rb_data_type_t function_data_type = { - .wrap_struct_name = "fiddle/function", - .function = { - .dmark = 0, - .dfree = deallocate, - .dsize = function_memsize - }, - .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, -}; - -static VALUE -allocate(VALUE klass) -{ - ffi_cif * cif; - - return TypedData_Make_Struct(klass, ffi_cif, &function_data_type, cif); -} - -VALUE -rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type) -{ - VALUE argv[3]; - - argv[0] = address; - argv[1] = arg_types; - argv[2] = ret_type; - - return rb_class_new_instance(3, argv, cFiddleFunction); -} - -static VALUE -normalize_argument_types(const char *name, - VALUE arg_types, - bool *is_variadic) -{ - VALUE normalized_arg_types; - int i; - int n_arg_types; - *is_variadic = false; - - Check_Type(arg_types, T_ARRAY); - n_arg_types = RARRAY_LENINT(arg_types); - Check_Max_Args(name, n_arg_types); - - normalized_arg_types = rb_ary_new_capa(n_arg_types); - for (i = 0; i < n_arg_types; i++) { - VALUE arg_type = RARRAY_AREF(arg_types, i); - int c_arg_type; - arg_type = rb_fiddle_type_ensure(arg_type); - c_arg_type = NUM2INT(arg_type); - if (c_arg_type == TYPE_VARIADIC) { - if (i != n_arg_types - 1) { - rb_raise(rb_eArgError, - "Fiddle::TYPE_VARIADIC must be the last argument type: " - "%"PRIsVALUE, - arg_types); - } - *is_variadic = true; - break; - } - else { - (void)INT2FFI_TYPE(c_arg_type); /* raise */ - } - rb_ary_push(normalized_arg_types, INT2FIX(c_arg_type)); - } - - /* freeze to prevent inconsistency at calling #to_int later */ - OBJ_FREEZE(normalized_arg_types); - return normalized_arg_types; -} - -static VALUE -initialize(int argc, VALUE argv[], VALUE self) -{ - ffi_cif * cif; - VALUE ptr, arg_types, ret_type, abi, kwargs; - VALUE name = Qnil; - VALUE need_gvl = Qfalse; - int c_ret_type; - bool is_variadic = false; - ffi_abi c_ffi_abi; - void *cfunc; - - rb_scan_args(argc, argv, "31:", &ptr, &arg_types, &ret_type, &abi, &kwargs); - rb_iv_set(self, "@closure", ptr); - - if (!NIL_P(kwargs)) { - enum { - kw_name, - kw_need_gvl, - kw_max_, - }; - static ID kw[kw_max_]; - VALUE args[kw_max_]; - if (!kw[0]) { - kw[kw_name] = rb_intern_const("name"); - kw[kw_need_gvl] = rb_intern_const("need_gvl"); - } - rb_get_kwargs(kwargs, kw, 0, kw_max_, args); - if (args[kw_name] != Qundef) { - name = args[kw_name]; - } - if (args[kw_need_gvl] != Qundef) { - need_gvl = args[kw_need_gvl]; - } - } - rb_iv_set(self, "@name", name); - rb_iv_set(self, "@need_gvl", need_gvl); - - ptr = rb_Integer(ptr); - cfunc = NUM2PTR(ptr); - PTR2NUM(cfunc); - c_ffi_abi = NIL_P(abi) ? FFI_DEFAULT_ABI : NUM2INT(abi); - abi = INT2FIX(c_ffi_abi); - ret_type = rb_fiddle_type_ensure(ret_type); - c_ret_type = NUM2INT(ret_type); - (void)INT2FFI_TYPE(c_ret_type); /* raise */ - ret_type = INT2FIX(c_ret_type); - - arg_types = normalize_argument_types("argument types", - arg_types, - &is_variadic); -#ifndef HAVE_FFI_PREP_CIF_VAR - if (is_variadic) { - rb_raise(rb_eNotImpError, - "ffi_prep_cif_var() is required in libffi " - "for variadic arguments"); - } -#endif - - rb_iv_set(self, "@ptr", ptr); - rb_iv_set(self, "@argument_types", arg_types); - rb_iv_set(self, "@return_type", ret_type); - rb_iv_set(self, "@abi", abi); - rb_iv_set(self, "@is_variadic", is_variadic ? Qtrue : Qfalse); - - TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif); - cif->arg_types = NULL; - - return self; -} - -struct nogvl_ffi_call_args { - ffi_cif *cif; - void (*fn)(void); - void **values; - fiddle_generic retval; -}; - -static void * -nogvl_ffi_call(void *ptr) -{ - struct nogvl_ffi_call_args *args = ptr; - - ffi_call(args->cif, args->fn, &args->retval, args->values); - - return NULL; -} - -static VALUE -function_call(int argc, VALUE argv[], VALUE self) -{ - struct nogvl_ffi_call_args args = { 0 }; - fiddle_generic *generic_args; - VALUE cfunc; - VALUE abi; - VALUE arg_types; - VALUE cPointer; - VALUE is_variadic; - VALUE need_gvl; - int n_arg_types; - int n_fixed_args = 0; - int n_call_args = 0; - int i; - int i_call; - VALUE converted_args = Qnil; - VALUE alloc_buffer = 0; - - cfunc = rb_iv_get(self, "@ptr"); - abi = rb_iv_get(self, "@abi"); - arg_types = rb_iv_get(self, "@argument_types"); - cPointer = rb_const_get(mFiddle, rb_intern("Pointer")); - is_variadic = rb_iv_get(self, "@is_variadic"); - need_gvl = rb_iv_get(self, "@need_gvl"); - - n_arg_types = RARRAY_LENINT(arg_types); - n_fixed_args = n_arg_types; - if (RTEST(is_variadic)) { - if (argc < n_arg_types) { - rb_error_arity(argc, n_arg_types, UNLIMITED_ARGUMENTS); - } - if (((argc - n_arg_types) % 2) != 0) { - rb_raise(rb_eArgError, - "variadic arguments must be type and value pairs: " - "%"PRIsVALUE, - rb_ary_new_from_values(argc, argv)); - } - n_call_args = n_arg_types + ((argc - n_arg_types) / 2); - } - else { - if (argc != n_arg_types) { - rb_error_arity(argc, n_arg_types, n_arg_types); - } - n_call_args = n_arg_types; - } - Check_Max_Args("the number of arguments", n_call_args); - - TypedData_Get_Struct(self, ffi_cif, &function_data_type, args.cif); - - if (is_variadic && args.cif->arg_types) { - xfree(args.cif->arg_types); - args.cif->arg_types = NULL; - } - - if (!args.cif->arg_types) { - VALUE fixed_arg_types = arg_types; - VALUE return_type; - int c_return_type; - ffi_type *ffi_return_type; - ffi_type **ffi_arg_types; - ffi_status result; - - arg_types = rb_ary_dup(fixed_arg_types); - for (i = n_fixed_args; i < argc; i += 2) { - VALUE arg_type = argv[i]; - int c_arg_type; - arg_type = rb_fiddle_type_ensure(arg_type); - c_arg_type = NUM2INT(arg_type); - (void)INT2FFI_TYPE(c_arg_type); /* raise */ - rb_ary_push(arg_types, INT2FIX(c_arg_type)); - } - - return_type = rb_iv_get(self, "@return_type"); - c_return_type = FIX2INT(return_type); - ffi_return_type = INT2FFI_TYPE(c_return_type); - - ffi_arg_types = xcalloc(n_call_args + 1, sizeof(ffi_type *)); - for (i_call = 0; i_call < n_call_args; i_call++) { - VALUE arg_type; - int c_arg_type; - arg_type = RARRAY_AREF(arg_types, i_call); - c_arg_type = FIX2INT(arg_type); - ffi_arg_types[i_call] = INT2FFI_TYPE(c_arg_type); - } - ffi_arg_types[i_call] = NULL; - - if (is_variadic) { -#ifdef HAVE_FFI_PREP_CIF_VAR - result = ffi_prep_cif_var(args.cif, - FIX2INT(abi), - n_fixed_args, - n_call_args, - ffi_return_type, - ffi_arg_types); -#else - /* This code is never used because ffi_prep_cif_var() - * availability check is done in #initialize. */ - result = FFI_BAD_TYPEDEF; -#endif - } - else { - result = ffi_prep_cif(args.cif, - FIX2INT(abi), - n_call_args, - ffi_return_type, - ffi_arg_types); - } - if (result != FFI_OK) { - xfree(ffi_arg_types); - args.cif->arg_types = NULL; - rb_raise(rb_eRuntimeError, "error creating CIF %d", result); - } - } - - generic_args = ALLOCV(alloc_buffer, - sizeof(fiddle_generic) * n_call_args + - sizeof(void *) * (n_call_args + 1)); - args.values = (void **)((char *)generic_args + - sizeof(fiddle_generic) * n_call_args); - - for (i = 0, i_call = 0; - i < argc && i_call < n_call_args; - i++, i_call++) { - VALUE arg_type; - int c_arg_type; - VALUE original_src; - VALUE src; - arg_type = RARRAY_AREF(arg_types, i_call); - c_arg_type = FIX2INT(arg_type); - if (i >= n_fixed_args) { - i++; - } - src = argv[i]; - - if (c_arg_type == TYPE_VOIDP) { - if (NIL_P(src)) { - src = INT2FIX(0); - } - else if (cPointer != CLASS_OF(src)) { - src = rb_funcall(cPointer, rb_intern("[]"), 1, src); - if (NIL_P(converted_args)) { - converted_args = rb_ary_new(); - } - rb_ary_push(converted_args, src); - } - src = rb_Integer(src); - } - - original_src = src; - VALUE2GENERIC(c_arg_type, src, &generic_args[i_call]); - if (src != original_src) { - if (NIL_P(converted_args)) { - converted_args = rb_ary_new(); - } - rb_ary_push(converted_args, src); - } - args.values[i_call] = (void *)&generic_args[i_call]; - } - args.values[i_call] = NULL; - args.fn = (void(*)(void))NUM2PTR(cfunc); - - if (RTEST(need_gvl)) { - ffi_call(args.cif, args.fn, &(args.retval), args.values); - } - else { - (void)rb_thread_call_without_gvl(nogvl_ffi_call, &args, 0, 0); - } - - { - int errno_keep = errno; -#if defined(_WIN32) - DWORD error = WSAGetLastError(); - int socket_error = WSAGetLastError(); - rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, - ULONG2NUM(error)); - rb_funcall(mFiddle, rb_intern("win32_last_socket_error="), 1, - INT2NUM(socket_error)); -#endif - rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno_keep)); - } - - ALLOCV_END(alloc_buffer); - - return GENERIC2VALUE(rb_iv_get(self, "@return_type"), args.retval); -} - -void -Init_fiddle_function(void) -{ - /* - * Document-class: Fiddle::Function - * - * == Description - * - * A representation of a C function - * - * == Examples - * - * === 'strcpy' - * - * @libc = Fiddle.dlopen "/lib/libc.so.6" - * #=> #<Fiddle::Handle:0x00000001d7a8d8> - * f = Fiddle::Function.new( - * @libc['strcpy'], - * [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP], - * Fiddle::TYPE_VOIDP) - * #=> #<Fiddle::Function:0x00000001d8ee00> - * buff = "000" - * #=> "000" - * str = f.call(buff, "123") - * #=> #<Fiddle::Pointer:0x00000001d0c380 ptr=0x000000018a21b8 size=0 free=0x00000000000000> - * str.to_s - * => "123" - * - * === ABI check - * - * @libc = Fiddle.dlopen "/lib/libc.so.6" - * #=> #<Fiddle::Handle:0x00000001d7a8d8> - * f = Fiddle::Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP) - * #=> #<Fiddle::Function:0x00000001d8ee00> - * f.abi == Fiddle::Function::DEFAULT - * #=> true - */ - cFiddleFunction = rb_define_class_under(mFiddle, "Function", rb_cObject); - - /* - * Document-const: DEFAULT - * - * Default ABI - * - */ - rb_define_const(cFiddleFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI)); - -#ifdef HAVE_CONST_FFI_STDCALL - /* - * Document-const: STDCALL - * - * FFI implementation of WIN32 stdcall convention - * - */ - rb_define_const(cFiddleFunction, "STDCALL", INT2NUM(FFI_STDCALL)); -#endif - - rb_define_alloc_func(cFiddleFunction, allocate); - - /* - * Document-method: call - * - * Calls the constructed Function, with +args+. - * Caller must ensure the underlying function is called in a - * thread-safe manner if running in a multi-threaded process. - * - * Note that it is not thread-safe to use this method to - * directly or indirectly call many Ruby C-extension APIs unless - * you don't pass +need_gvl: true+ to Fiddle::Function#new. - * - * For an example see Fiddle::Function - * - */ - rb_define_method(cFiddleFunction, "call", function_call, -1); - - /* - * Document-method: new - * call-seq: new(ptr, - * args, - * ret_type, - * abi = DEFAULT, - * name: nil, - * need_gvl: false) - * - * Constructs a Function object. - * * +ptr+ is a referenced function, of a Fiddle::Handle - * * +args+ is an Array of arguments, passed to the +ptr+ function - * * +ret_type+ is the return type of the function - * * +abi+ is the ABI of the function - * * +name+ is the name of the function - * * +need_gvl+ is whether GVL is needed to call the function - * - */ - rb_define_method(cFiddleFunction, "initialize", initialize, -1); -} -/* vim: set noet sws=4 sw=4: */ diff --git a/ext/fiddle/function.h b/ext/fiddle/function.h deleted file mode 100644 index 829e592c8a..0000000000 --- a/ext/fiddle/function.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef FIDDLE_FUNCTION_H -#define FIDDLE_FUNCTION_H - -#include <fiddle.h> - -void Init_fiddle_function(void); - -#endif diff --git a/ext/fiddle/handle.c b/ext/fiddle/handle.c deleted file mode 100644 index 8ba416952a..0000000000 --- a/ext/fiddle/handle.c +++ /dev/null @@ -1,591 +0,0 @@ -#include <ruby.h> -#include <fiddle.h> - -VALUE rb_cHandle; - -struct dl_handle { - void *ptr; - int open; - int enable_close; -}; - -#ifdef _WIN32 -# ifndef _WIN32_WCE -static void * -w32_coredll(void) -{ - MEMORY_BASIC_INFORMATION m; - memset(&m, 0, sizeof(m)); - if( !VirtualQuery(_errno, &m, sizeof(m)) ) return NULL; - return m.AllocationBase; -} -# endif - -static int -w32_dlclose(void *ptr) -{ -# ifndef _WIN32_WCE - if( ptr == w32_coredll() ) return 0; -# endif - if( FreeLibrary((HMODULE)ptr) ) return 0; - return errno = rb_w32_map_errno(GetLastError()); -} -#define dlclose(ptr) w32_dlclose(ptr) -#endif - -static void -fiddle_handle_free(void *ptr) -{ - struct dl_handle *fiddle_handle = ptr; - if( fiddle_handle->ptr && fiddle_handle->open && fiddle_handle->enable_close ){ - dlclose(fiddle_handle->ptr); - } - xfree(ptr); -} - -static size_t -fiddle_handle_memsize(const void *ptr) -{ - return sizeof(struct dl_handle); -} - -static const rb_data_type_t fiddle_handle_data_type = { - .wrap_struct_name = "fiddle/handle", - .function = { - .dmark = 0, - .dfree = fiddle_handle_free, - .dsize = fiddle_handle_memsize - }, - .flags = RUBY_TYPED_WB_PROTECTED, -}; - -/* - * call-seq: close - * - * Close this handle. - * - * Calling close more than once will raise a Fiddle::DLError exception. - */ -static VALUE -rb_fiddle_handle_close(VALUE self) -{ - struct dl_handle *fiddle_handle; - - TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); - if(fiddle_handle->open) { - int ret = dlclose(fiddle_handle->ptr); - fiddle_handle->open = 0; - - /* Check dlclose for successful return value */ - if(ret) { -#if defined(HAVE_DLERROR) - rb_raise(rb_eFiddleDLError, "%s", dlerror()); -#else - rb_raise(rb_eFiddleDLError, "could not close handle"); -#endif - } - return INT2NUM(ret); - } - rb_raise(rb_eFiddleDLError, "dlclose() called too many times"); - - UNREACHABLE; -} - -static VALUE -rb_fiddle_handle_s_allocate(VALUE klass) -{ - VALUE obj; - struct dl_handle *fiddle_handle; - - obj = TypedData_Make_Struct(rb_cHandle, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); - fiddle_handle->ptr = 0; - fiddle_handle->open = 0; - fiddle_handle->enable_close = 0; - - return obj; -} - -static VALUE -predefined_fiddle_handle(void *handle) -{ - VALUE obj = rb_fiddle_handle_s_allocate(rb_cHandle); - struct dl_handle *fiddle_handle = DATA_PTR(obj); - - fiddle_handle->ptr = handle; - fiddle_handle->open = 1; - OBJ_FREEZE(obj); - return obj; -} - -/* - * call-seq: - * new(library = nil, flags = Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL) - * - * Create a new handler that opens +library+ with +flags+. - * - * If no +library+ is specified or +nil+ is given, DEFAULT is used, which is - * the equivalent to RTLD_DEFAULT. See <code>man 3 dlopen</code> for more. - * - * lib = Fiddle::Handle.new - * - * The default is dependent on OS, and provide a handle for all libraries - * already loaded. For example, in most cases you can use this to access +libc+ - * functions, or ruby functions like +rb_str_new+. - */ -static VALUE -rb_fiddle_handle_initialize(int argc, VALUE argv[], VALUE self) -{ - void *ptr; - struct dl_handle *fiddle_handle; - VALUE lib, flag; - char *clib; - int cflag; - const char *err; - - switch( rb_scan_args(argc, argv, "02", &lib, &flag) ){ - case 0: - clib = NULL; - cflag = RTLD_LAZY | RTLD_GLOBAL; - break; - case 1: - clib = NIL_P(lib) ? NULL : StringValueCStr(lib); - cflag = RTLD_LAZY | RTLD_GLOBAL; - break; - case 2: - clib = NIL_P(lib) ? NULL : StringValueCStr(lib); - cflag = NUM2INT(flag); - break; - default: - rb_bug("rb_fiddle_handle_new"); - } - -#if defined(_WIN32) - if( !clib ){ - HANDLE rb_libruby_handle(void); - ptr = rb_libruby_handle(); - } - else if( STRCASECMP(clib, "libc") == 0 -# ifdef RUBY_COREDLL - || STRCASECMP(clib, RUBY_COREDLL) == 0 - || STRCASECMP(clib, RUBY_COREDLL".dll") == 0 -# endif - ){ -# ifdef _WIN32_WCE - ptr = dlopen("coredll.dll", cflag); -# else - (void)cflag; - ptr = w32_coredll(); -# endif - } - else -#endif - ptr = dlopen(clib, cflag); -#if defined(HAVE_DLERROR) - if( !ptr && (err = dlerror()) ){ - rb_raise(rb_eFiddleDLError, "%s", err); - } -#else - if( !ptr ){ - err = dlerror(); - rb_raise(rb_eFiddleDLError, "%s", err); - } -#endif - TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); - if( fiddle_handle->ptr && fiddle_handle->open && fiddle_handle->enable_close ){ - dlclose(fiddle_handle->ptr); - } - fiddle_handle->ptr = ptr; - fiddle_handle->open = 1; - fiddle_handle->enable_close = 0; - - if( rb_block_given_p() ){ - rb_ensure(rb_yield, self, rb_fiddle_handle_close, self); - } - - return Qnil; -} - -/* - * call-seq: enable_close - * - * Enable a call to dlclose() when this handle is garbage collected. - */ -static VALUE -rb_fiddle_handle_enable_close(VALUE self) -{ - struct dl_handle *fiddle_handle; - - TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); - fiddle_handle->enable_close = 1; - return Qnil; -} - -/* - * call-seq: disable_close - * - * Disable a call to dlclose() when this handle is garbage collected. - */ -static VALUE -rb_fiddle_handle_disable_close(VALUE self) -{ - struct dl_handle *fiddle_handle; - - TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); - fiddle_handle->enable_close = 0; - return Qnil; -} - -/* - * call-seq: close_enabled? - * - * Returns +true+ if dlclose() will be called when this handle is garbage collected. - * - * See man(3) dlclose() for more info. - */ -static VALUE -rb_fiddle_handle_close_enabled_p(VALUE self) -{ - struct dl_handle *fiddle_handle; - - TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); - - if(fiddle_handle->enable_close) return Qtrue; - return Qfalse; -} - -/* - * call-seq: to_i - * - * Returns the memory address for this handle. - */ -static VALUE -rb_fiddle_handle_to_i(VALUE self) -{ - struct dl_handle *fiddle_handle; - - TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); - return PTR2NUM(fiddle_handle->ptr); -} - -/* - * call-seq: to_ptr - * - * Returns the Fiddle::Pointer of this handle. - */ -static VALUE -rb_fiddle_handle_to_ptr(VALUE self) -{ - struct dl_handle *fiddle_handle; - - TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); - return rb_fiddle_ptr_new_wrap(fiddle_handle->ptr, 0, 0, self, 0); -} - -static VALUE fiddle_handle_sym(void *handle, VALUE symbol); - -/* - * Document-method: sym - * - * call-seq: sym(name) - * - * Get the address as an Integer for the function named +name+. - */ -static VALUE -rb_fiddle_handle_sym(VALUE self, VALUE sym) -{ - struct dl_handle *fiddle_handle; - - TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); - if( ! fiddle_handle->open ){ - rb_raise(rb_eFiddleDLError, "closed handle"); - } - - return fiddle_handle_sym(fiddle_handle->ptr, sym); -} - -#ifndef RTLD_NEXT -#define RTLD_NEXT NULL -#endif -#ifndef RTLD_DEFAULT -#define RTLD_DEFAULT NULL -#endif - -/* - * Document-method: sym - * - * call-seq: sym(name) - * - * Get the address as an Integer for the function named +name+. The function - * is searched via dlsym on RTLD_NEXT. - * - * See man(3) dlsym() for more info. - */ -static VALUE -rb_fiddle_handle_s_sym(VALUE self, VALUE sym) -{ - return fiddle_handle_sym(RTLD_NEXT, sym); -} - -typedef void (*fiddle_void_func)(void); - -static fiddle_void_func -fiddle_handle_find_func(void *handle, VALUE symbol) -{ -#if defined(HAVE_DLERROR) - const char *err; -# define CHECK_DLERROR if ((err = dlerror()) != 0) { func = 0; } -#else -# define CHECK_DLERROR -#endif - fiddle_void_func func; - const char *name = StringValueCStr(symbol); - -#ifdef HAVE_DLERROR - dlerror(); -#endif - func = (fiddle_void_func)(VALUE)dlsym(handle, name); - CHECK_DLERROR; -#if defined(FUNC_STDCALL) - if( !func ){ - int i; - int len = (int)strlen(name); - char *name_n; -#if defined(__CYGWIN__) || defined(_WIN32) || defined(__MINGW32__) - { - char *name_a = (char*)xmalloc(len+2); - strcpy(name_a, name); - name_n = name_a; - name_a[len] = 'A'; - name_a[len+1] = '\0'; - func = dlsym(handle, name_a); - CHECK_DLERROR; - if( func ) goto found; - name_n = xrealloc(name_a, len+6); - } -#else - name_n = (char*)xmalloc(len+6); -#endif - memcpy(name_n, name, len); - name_n[len++] = '@'; - for( i = 0; i < 256; i += 4 ){ - sprintf(name_n + len, "%d", i); - func = dlsym(handle, name_n); - CHECK_DLERROR; - if( func ) break; - } - if( func ) goto found; - name_n[len-1] = 'A'; - name_n[len++] = '@'; - for( i = 0; i < 256; i += 4 ){ - sprintf(name_n + len, "%d", i); - func = dlsym(handle, name_n); - CHECK_DLERROR; - if( func ) break; - } - found: - xfree(name_n); - } -#endif - - return func; -} - -static VALUE -rb_fiddle_handle_s_sym_defined(VALUE self, VALUE sym) -{ - fiddle_void_func func; - - func = fiddle_handle_find_func(RTLD_NEXT, sym); - - if( func ) { - return PTR2NUM(func); - } - else { - return Qnil; - } -} - -static VALUE -rb_fiddle_handle_sym_defined(VALUE self, VALUE sym) -{ - struct dl_handle *fiddle_handle; - fiddle_void_func func; - - TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); - if( ! fiddle_handle->open ){ - rb_raise(rb_eFiddleDLError, "closed handle"); - } - - func = fiddle_handle_find_func(fiddle_handle->ptr, sym); - - if( func ) { - return PTR2NUM(func); - } - else { - return Qnil; - } -} - -static VALUE -fiddle_handle_sym(void *handle, VALUE symbol) -{ - fiddle_void_func func; - - func = fiddle_handle_find_func(handle, symbol); - - if( !func ){ - rb_raise(rb_eFiddleDLError, "unknown symbol \"%"PRIsVALUE"\"", symbol); - } - - return PTR2NUM(func); -} - -/* - * call-seq: file_name - * - * Returns the file name of this handle. - */ -static VALUE -rb_fiddle_handle_file_name(VALUE self) -{ - struct dl_handle *fiddle_handle; - - TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); - -#if defined(HAVE_DLINFO) && defined(HAVE_CONST_RTLD_DI_LINKMAP) - { - struct link_map *lm = NULL; - int res = dlinfo(fiddle_handle->ptr, RTLD_DI_LINKMAP, &lm); - if (res == 0 && lm != NULL) { - return rb_str_new_cstr(lm->l_name); - } - else { -#if defined(HAVE_DLERROR) - rb_raise(rb_eFiddleDLError, "could not get handle file name: %s", dlerror()); -#else - rb_raise(rb_eFiddleDLError, "could not get handle file name"); -#endif - } - } -#elif defined(HAVE_GETMODULEFILENAME) - { - char filename[MAX_PATH]; - DWORD res = GetModuleFileName(fiddle_handle->ptr, filename, MAX_PATH); - if (res == 0) { - rb_raise(rb_eFiddleDLError, "could not get handle file name: %s", dlerror()); - } - return rb_str_new_cstr(filename); - } -#else - (void)fiddle_handle; - return Qnil; -#endif -} - -void -Init_fiddle_handle(void) -{ - /* - * Document-class: Fiddle::Handle - * - * The Fiddle::Handle is the manner to access the dynamic library - * - * == Example - * - * === Setup - * - * libc_so = "/lib64/libc.so.6" - * => "/lib64/libc.so.6" - * @handle = Fiddle::Handle.new(libc_so) - * => #<Fiddle::Handle:0x00000000d69ef8> - * - * === Setup, with flags - * - * libc_so = "/lib64/libc.so.6" - * => "/lib64/libc.so.6" - * @handle = Fiddle::Handle.new(libc_so, Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL) - * => #<Fiddle::Handle:0x00000000d69ef8> - * - * See RTLD_LAZY and RTLD_GLOBAL - * - * === Addresses to symbols - * - * strcpy_addr = @handle['strcpy'] - * => 140062278451968 - * - * or - * - * strcpy_addr = @handle.sym('strcpy') - * => 140062278451968 - * - */ - rb_cHandle = rb_define_class_under(mFiddle, "Handle", rb_cObject); - rb_define_alloc_func(rb_cHandle, rb_fiddle_handle_s_allocate); - rb_define_singleton_method(rb_cHandle, "sym", rb_fiddle_handle_s_sym, 1); - rb_define_singleton_method(rb_cHandle, "sym_defined?", rb_fiddle_handle_s_sym_defined, 1); - rb_define_singleton_method(rb_cHandle, "[]", rb_fiddle_handle_s_sym, 1); - - /* Document-const: NEXT - * - * A predefined pseudo-handle of RTLD_NEXT - * - * Which will find the next occurrence of a function in the search order - * after the current library. - */ - rb_define_const(rb_cHandle, "NEXT", predefined_fiddle_handle(RTLD_NEXT)); - - /* Document-const: DEFAULT - * - * A predefined pseudo-handle of RTLD_DEFAULT - * - * Which will find the first occurrence of the desired symbol using the - * default library search order - */ - rb_define_const(rb_cHandle, "DEFAULT", predefined_fiddle_handle(RTLD_DEFAULT)); - - /* Document-const: RTLD_GLOBAL - * - * rtld Fiddle::Handle flag. - * - * The symbols defined by this library will be made available for symbol - * resolution of subsequently loaded libraries. - */ - rb_define_const(rb_cHandle, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL)); - - /* Document-const: RTLD_LAZY - * - * rtld Fiddle::Handle flag. - * - * Perform lazy binding. Only resolve symbols as the code that references - * them is executed. If the symbol is never referenced, then it is never - * resolved. (Lazy binding is only performed for function references; - * references to variables are always immediately bound when the library - * is loaded.) - */ - rb_define_const(rb_cHandle, "RTLD_LAZY", INT2NUM(RTLD_LAZY)); - - /* Document-const: RTLD_NOW - * - * rtld Fiddle::Handle flag. - * - * If this value is specified or the environment variable LD_BIND_NOW is - * set to a nonempty string, all undefined symbols in the library are - * resolved before Fiddle.dlopen returns. If this cannot be done an error - * is returned. - */ - rb_define_const(rb_cHandle, "RTLD_NOW", INT2NUM(RTLD_NOW)); - - rb_define_method(rb_cHandle, "initialize", rb_fiddle_handle_initialize, -1); - rb_define_method(rb_cHandle, "to_i", rb_fiddle_handle_to_i, 0); - rb_define_method(rb_cHandle, "to_ptr", rb_fiddle_handle_to_ptr, 0); - rb_define_method(rb_cHandle, "close", rb_fiddle_handle_close, 0); - rb_define_method(rb_cHandle, "sym", rb_fiddle_handle_sym, 1); - rb_define_method(rb_cHandle, "[]", rb_fiddle_handle_sym, 1); - rb_define_method(rb_cHandle, "sym_defined?", rb_fiddle_handle_sym_defined, 1); - rb_define_method(rb_cHandle, "file_name", rb_fiddle_handle_file_name, 0); - rb_define_method(rb_cHandle, "disable_close", rb_fiddle_handle_disable_close, 0); - rb_define_method(rb_cHandle, "enable_close", rb_fiddle_handle_enable_close, 0); - rb_define_method(rb_cHandle, "close_enabled?", rb_fiddle_handle_close_enabled_p, 0); -} - -/* vim: set noet sws=4 sw=4: */ diff --git a/ext/fiddle/lib/fiddle.rb b/ext/fiddle/lib/fiddle.rb deleted file mode 100644 index 6137c487c6..0000000000 --- a/ext/fiddle/lib/fiddle.rb +++ /dev/null @@ -1,103 +0,0 @@ -# frozen_string_literal: true - -require 'fiddle.so' -require 'fiddle/closure' -require 'fiddle/function' -require 'fiddle/version' - -module Fiddle - if WINDOWS - # Returns the last win32 +Error+ of the current executing +Thread+ or nil - # if none - def self.win32_last_error - Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] - end - - # Sets the last win32 +Error+ of the current executing +Thread+ to +error+ - def self.win32_last_error= error - Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] = error - end - - # Returns the last win32 socket +Error+ of the current executing - # +Thread+ or nil if none - def self.win32_last_socket_error - Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__] - end - - # Sets the last win32 socket +Error+ of the current executing - # +Thread+ to +error+ - def self.win32_last_socket_error= error - Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__] = error - end - end - - # Returns the last +Error+ of the current executing +Thread+ or nil if none - def self.last_error - Thread.current[:__FIDDLE_LAST_ERROR__] - end - - # Sets the last +Error+ of the current executing +Thread+ to +error+ - def self.last_error= error - Thread.current[:__DL2_LAST_ERROR__] = error - Thread.current[:__FIDDLE_LAST_ERROR__] = error - end - - # call-seq: dlopen(library) => Fiddle::Handle - # - # Creates a new handler that opens +library+, and returns an instance of - # Fiddle::Handle. - # - # If +nil+ is given for the +library+, Fiddle::Handle::DEFAULT is used, which - # is the equivalent to RTLD_DEFAULT. See <code>man 3 dlopen</code> for more. - # - # lib = Fiddle.dlopen(nil) - # - # The default is dependent on OS, and provide a handle for all libraries - # already loaded. For example, in most cases you can use this to access - # +libc+ functions, or ruby functions like +rb_str_new+. - # - # See Fiddle::Handle.new for more. - def dlopen library - begin - Fiddle::Handle.new(library) - rescue DLError => error - case RUBY_PLATFORM - when /linux/ - case error.message - when /\A(\/.+?): (?:invalid ELF header|file too short)/ - # This may be a linker script: - # https://sourceware.org/binutils/docs/ld.html#Scripts - path = $1 - else - raise - end - else - raise - end - - File.open(path) do |input| - input.each_line do |line| - case line - when /\A\s*(?:INPUT|GROUP)\s*\(\s*([^\s,\)]+)/ - # TODO: Should we support multiple files? - return dlopen($1) - end - end - end - - # Not found - raise - end - end - module_function :dlopen - - # Add constants for backwards compat - - RTLD_GLOBAL = Handle::RTLD_GLOBAL # :nodoc: - RTLD_LAZY = Handle::RTLD_LAZY # :nodoc: - RTLD_NOW = Handle::RTLD_NOW # :nodoc: - - Fiddle::Types.constants.each do |type| - const_set "TYPE_#{type}", Fiddle::Types.const_get(type) - end -end diff --git a/ext/fiddle/lib/fiddle/closure.rb b/ext/fiddle/lib/fiddle/closure.rb deleted file mode 100644 index 7e0077ea52..0000000000 --- a/ext/fiddle/lib/fiddle/closure.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true -module Fiddle - class Closure - class << self - # Create a new closure. If a block is given, the created closure - # is automatically freed after the given block is executed. - # - # The all given arguments are passed to Fiddle::Closure.new. So - # using this method without block equals to Fiddle::Closure.new. - # - # == Example - # - # Fiddle::Closure.create(TYPE_INT, [TYPE_INT]) do |closure| - # # closure is freed automatically when this block is finished. - # end - def create(*args) - if block_given? - closure = new(*args) - begin - yield(closure) - ensure - closure.free - end - else - new(*args) - end - end - end - - # the C type of the return of the FFI closure - attr_reader :ctype - - # arguments of the FFI closure - attr_reader :args - - # Extends Fiddle::Closure to allow for building the closure in a block - class BlockCaller < Fiddle::Closure - - # == Description - # - # Construct a new BlockCaller object. - # - # * +ctype+ is the C type to be returned - # * +args+ are passed the callback - # * +abi+ is the abi of the closure - # - # If there is an error in preparing the +ffi_cif+ or +ffi_prep_closure+, - # then a RuntimeError will be raised. - # - # == Example - # - # include Fiddle - # - # cb = Closure::BlockCaller.new(TYPE_INT, [TYPE_INT]) do |one| - # one - # end - # - # func = Function.new(cb, [TYPE_INT], TYPE_INT) - # - def initialize ctype, args, abi = Fiddle::Function::DEFAULT, &block - super(ctype, args, abi) - @block = block - end - - # Calls the constructed BlockCaller, with +args+ - # - # For an example see Fiddle::Closure::BlockCaller.new - # - def call *args - @block.call(*args) - end - end - end -end diff --git a/ext/fiddle/lib/fiddle/cparser.rb b/ext/fiddle/lib/fiddle/cparser.rb deleted file mode 100644 index 264ca166dd..0000000000 --- a/ext/fiddle/lib/fiddle/cparser.rb +++ /dev/null @@ -1,278 +0,0 @@ -# frozen_string_literal: true -module Fiddle - # A mixin that provides methods for parsing C struct and prototype signatures. - # - # == Example - # require 'fiddle/import' - # - # include Fiddle::CParser - # #=> Object - # - # parse_ctype('int') - # #=> Fiddle::TYPE_INT - # - # parse_struct_signature(['int i', 'char c']) - # #=> [[Fiddle::TYPE_INT, Fiddle::TYPE_CHAR], ["i", "c"]] - # - # parse_signature('double sum(double, double)') - # #=> ["sum", Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE, Fiddle::TYPE_DOUBLE]] - # - module CParser - # Parses a C struct's members - # - # Example: - # require 'fiddle/import' - # - # include Fiddle::CParser - # #=> Object - # - # parse_struct_signature(['int i', 'char c']) - # #=> [[Fiddle::TYPE_INT, Fiddle::TYPE_CHAR], ["i", "c"]] - # - # parse_struct_signature(['char buffer[80]']) - # #=> [[[Fiddle::TYPE_CHAR, 80]], ["buffer"]] - # - def parse_struct_signature(signature, tymap=nil) - if signature.is_a?(String) - signature = split_arguments(signature, /[,;]/) - elsif signature.is_a?(Hash) - signature = [signature] - end - mems = [] - tys = [] - signature.each{|msig| - msig = compact(msig) if msig.is_a?(String) - case msig - when Hash - msig.each do |struct_name, struct_signature| - struct_name = struct_name.to_s if struct_name.is_a?(Symbol) - struct_name = compact(struct_name) - struct_count = nil - if struct_name =~ /^([\w\*\s]+)\[(\d+)\]$/ - struct_count = $2.to_i - struct_name = $1 - end - if struct_signature.respond_to?(:entity_class) - struct_type = struct_signature - else - parsed_struct = parse_struct_signature(struct_signature, tymap) - struct_type = CStructBuilder.create(CStruct, *parsed_struct) - end - if struct_count - ty = [struct_type, struct_count] - else - ty = struct_type - end - mems.push([struct_name, struct_type.members]) - tys.push(ty) - end - when /^[\w\*\s]+[\*\s](\w+)$/ - mems.push($1) - tys.push(parse_ctype(msig, tymap)) - when /^[\w\*\s]+\(\*(\w+)\)\(.*?\)$/ - mems.push($1) - tys.push(parse_ctype(msig, tymap)) - when /^([\w\*\s]+[\*\s])(\w+)\[(\d+)\]$/ - mems.push($2) - tys.push([parse_ctype($1.strip, tymap), $3.to_i]) - when /^([\w\*\s]+)\[(\d+)\](\w+)$/ - mems.push($3) - tys.push([parse_ctype($1.strip, tymap), $2.to_i]) - else - raise(RuntimeError,"can't parse the struct member: #{msig}") - end - } - return tys, mems - end - - # Parses a C prototype signature - # - # If Hash +tymap+ is provided, the return value and the arguments from the - # +signature+ are expected to be keys, and the value will be the C type to - # be looked up. - # - # Example: - # require 'fiddle/import' - # - # include Fiddle::CParser - # #=> Object - # - # parse_signature('double sum(double, double)') - # #=> ["sum", Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE, Fiddle::TYPE_DOUBLE]] - # - # parse_signature('void update(void (*cb)(int code))') - # #=> ["update", Fiddle::TYPE_VOID, [Fiddle::TYPE_VOIDP]] - # - # parse_signature('char (*getbuffer(void))[80]') - # #=> ["getbuffer", Fiddle::TYPE_VOIDP, []] - # - def parse_signature(signature, tymap=nil) - tymap ||= {} - case compact(signature) - when /^(?:[\w\*\s]+)\(\*(\w+)\((.*?)\)\)(?:\[\w*\]|\(.*?\));?$/ - func, args = $1, $2 - return [func, TYPE_VOIDP, split_arguments(args).collect {|arg| parse_ctype(arg, tymap)}] - when /^([\w\*\s]+[\*\s])(\w+)\((.*?)\);?$/ - ret, func, args = $1.strip, $2, $3 - return [func, parse_ctype(ret, tymap), split_arguments(args).collect {|arg| parse_ctype(arg, tymap)}] - else - raise(RuntimeError,"can't parse the function prototype: #{signature}") - end - end - - # Given a String of C type +ty+, returns the corresponding Fiddle constant. - # - # +ty+ can also accept an Array of C type Strings, and will be returned in - # a corresponding Array. - # - # If Hash +tymap+ is provided, +ty+ is expected to be the key, and the - # value will be the C type to be looked up. - # - # Example: - # require 'fiddle/import' - # - # include Fiddle::CParser - # #=> Object - # - # parse_ctype('int') - # #=> Fiddle::TYPE_INT - # - # parse_ctype('double diff') - # #=> Fiddle::TYPE_DOUBLE - # - # parse_ctype('unsigned char byte') - # #=> -Fiddle::TYPE_CHAR - # - # parse_ctype('const char* const argv[]') - # #=> -Fiddle::TYPE_VOIDP - # - def parse_ctype(ty, tymap=nil) - tymap ||= {} - if ty.is_a?(Array) - return [parse_ctype(ty[0], tymap), ty[1]] - end - ty = ty.gsub(/\Aconst\s+/, "") - case ty - when 'void' - return TYPE_VOID - when /\A(?:(?:signed\s+)?long\s+long(?:\s+int\s+)?|int64_t)(?:\s+\w+)?\z/ - unless Fiddle.const_defined?(:TYPE_LONG_LONG) - raise(RuntimeError, "unsupported type: #{ty}") - end - return TYPE_LONG_LONG - when /\A(?:unsigned\s+long\s+long(?:\s+int\s+)?|uint64_t)(?:\s+\w+)?\z/ - unless Fiddle.const_defined?(:TYPE_LONG_LONG) - raise(RuntimeError, "unsupported type: #{ty}") - end - return TYPE_ULONG_LONG - when /\Aunsigned\s+long(?:\s+int\s+)?(?:\s+\w+)?\z/, - /\Aunsigned\s+int\s+long(?:\s+\w+)?\z/, - /\Along(?:\s+int)?\s+unsigned(?:\s+\w+)?\z/, - /\Aint\s+unsigned\s+long(?:\s+\w+)?\z/, - /\A(?:int\s+)?long\s+unsigned(?:\s+\w+)?\z/ - return TYPE_ULONG - when /\A(?:signed\s+)?long(?:\s+int\s+)?(?:\s+\w+)?\z/, - /\A(?:signed\s+)?int\s+long(?:\s+\w+)?\z/, - /\Along(?:\s+int)?\s+signed(?:\s+\w+)?\z/ - return TYPE_LONG - when /\Aunsigned\s+short(?:\s+int\s+)?(?:\s+\w+)?\z/, - /\Aunsigned\s+int\s+short(?:\s+\w+)?\z/, - /\Ashort(?:\s+int)?\s+unsigned(?:\s+\w+)?\z/, - /\Aint\s+unsigned\s+short(?:\s+\w+)?\z/, - /\A(?:int\s+)?short\s+unsigned(?:\s+\w+)?\z/ - return TYPE_USHORT - when /\A(?:signed\s+)?short(?:\s+int\s+)?(?:\s+\w+)?\z/, - /\A(?:signed\s+)?int\s+short(?:\s+\w+)?\z/, - /\Aint\s+(?:signed\s+)?short(?:\s+\w+)?\z/ - return TYPE_SHORT - when /\A(?:signed\s+)?int(?:\s+\w+)?\z/ - return TYPE_INT - when /\A(?:unsigned\s+int|uint)(?:\s+\w+)?\z/ - return TYPE_UINT - when /\A(?:signed\s+)?char(?:\s+\w+)?\z/ - return TYPE_CHAR - when /\Aunsigned\s+char(?:\s+\w+)?\z/ - return TYPE_UCHAR - when /\Aint8_t(?:\s+\w+)?\z/ - unless Fiddle.const_defined?(:TYPE_INT8_T) - raise(RuntimeError, "unsupported type: #{ty}") - end - return TYPE_INT8_T - when /\Auint8_t(?:\s+\w+)?\z/ - unless Fiddle.const_defined?(:TYPE_INT8_T) - raise(RuntimeError, "unsupported type: #{ty}") - end - return TYPE_UINT8_T - when /\Aint16_t(?:\s+\w+)?\z/ - unless Fiddle.const_defined?(:TYPE_INT16_T) - raise(RuntimeError, "unsupported type: #{ty}") - end - return TYPE_INT16_T - when /\Auint16_t(?:\s+\w+)?\z/ - unless Fiddle.const_defined?(:TYPE_INT16_T) - raise(RuntimeError, "unsupported type: #{ty}") - end - return TYPE_UINT16_T - when /\Aint32_t(?:\s+\w+)?\z/ - unless Fiddle.const_defined?(:TYPE_INT32_T) - raise(RuntimeError, "unsupported type: #{ty}") - end - return TYPE_INT32_T - when /\Auint32_t(?:\s+\w+)?\z/ - unless Fiddle.const_defined?(:TYPE_INT32_T) - raise(RuntimeError, "unsupported type: #{ty}") - end - return TYPE_UINT32_T - when /\Aint64_t(?:\s+\w+)?\z/ - unless Fiddle.const_defined?(:TYPE_INT64_T) - raise(RuntimeError, "unsupported type: #{ty}") - end - return TYPE_INT64_T - when /\Auint64_t(?:\s+\w+)?\z/ - unless Fiddle.const_defined?(:TYPE_INT64_T) - raise(RuntimeError, "unsupported type: #{ty}") - end - return TYPE_UINT64_T - when /\Afloat(?:\s+\w+)?\z/ - return TYPE_FLOAT - when /\Adouble(?:\s+\w+)?\z/ - return TYPE_DOUBLE - when /\Asize_t(?:\s+\w+)?\z/ - return TYPE_SIZE_T - when /\Assize_t(?:\s+\w+)?\z/ - return TYPE_SSIZE_T - when /\Aptrdiff_t(?:\s+\w+)?\z/ - return TYPE_PTRDIFF_T - when /\Aintptr_t(?:\s+\w+)?\z/ - return TYPE_INTPTR_T - when /\Auintptr_t(?:\s+\w+)?\z/ - return TYPE_UINTPTR_T - when "bool" - return TYPE_BOOL - when /\*/, /\[[\s\d]*\]/ - return TYPE_VOIDP - when "..." - return TYPE_VARIADIC - else - ty = ty.split(' ', 2)[0] - if( tymap[ty] ) - return parse_ctype(tymap[ty], tymap) - else - raise(DLError, "unknown type: #{ty}") - end - end - end - - private - - def split_arguments(arguments, sep=',') - return [] if arguments.strip == 'void' - arguments.scan(/([\w\*\s]+\(\*\w*\)\(.*?\)|[\w\*\s\[\]]+|\.\.\.)(?:#{sep}\s*|\z)/).collect {|m| m[0]} - end - - def compact(signature) - signature.gsub(/\s+/, ' ').gsub(/\s*([\(\)\[\]\*,;])\s*/, '\1').strip - end - - end -end diff --git a/ext/fiddle/lib/fiddle/function.rb b/ext/fiddle/lib/fiddle/function.rb deleted file mode 100644 index dc2e3e6bf5..0000000000 --- a/ext/fiddle/lib/fiddle/function.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true -module Fiddle - class Function - # The ABI of the Function. - attr_reader :abi - - # The address of this function - attr_reader :ptr - - # The name of this function - attr_reader :name - - # Whether GVL is needed to call this function - def need_gvl? - @need_gvl - end - - # The integer memory location of this function - def to_i - ptr.to_i - end - - # Turn this function in to a proc - def to_proc - this = self - lambda { |*args| this.call(*args) } - end - end -end diff --git a/ext/fiddle/lib/fiddle/import.rb b/ext/fiddle/lib/fiddle/import.rb deleted file mode 100644 index 050708fb96..0000000000 --- a/ext/fiddle/lib/fiddle/import.rb +++ /dev/null @@ -1,322 +0,0 @@ -# frozen_string_literal: true -require 'fiddle' -require 'fiddle/struct' -require 'fiddle/cparser' - -module Fiddle - - # Used internally by Fiddle::Importer - class CompositeHandler - # Create a new handler with the open +handlers+ - # - # Used internally by Fiddle::Importer.dlload - def initialize(handlers) - @handlers = handlers - end - - # Array of the currently loaded libraries. - def handlers() - @handlers - end - - # Returns the address as an Integer from any handlers with the function - # named +symbol+. - # - # Raises a DLError if the handle is closed. - def sym(symbol) - @handlers.each{|handle| - if( handle ) - begin - addr = handle.sym(symbol) - return addr - rescue DLError - end - end - } - return nil - end - - # See Fiddle::CompositeHandler.sym - def [](symbol) - sym(symbol) - end - end - - # A DSL that provides the means to dynamically load libraries and build - # modules around them including calling extern functions within the C - # library that has been loaded. - # - # == Example - # - # require 'fiddle' - # require 'fiddle/import' - # - # module LibSum - # extend Fiddle::Importer - # dlload './libsum.so' - # extern 'double sum(double*, int)' - # extern 'double split(double)' - # end - # - module Importer - include Fiddle - include CParser - extend Importer - - attr_reader :type_alias - private :type_alias - - # Creates an array of handlers for the given +libs+, can be an instance of - # Fiddle::Handle, Fiddle::Importer, or will create a new instance of - # Fiddle::Handle using Fiddle.dlopen - # - # Raises a DLError if the library cannot be loaded. - # - # See Fiddle.dlopen - def dlload(*libs) - handles = libs.collect{|lib| - case lib - when nil - nil - when Handle - lib - when Importer - lib.handlers - else - Fiddle.dlopen(lib) - end - }.flatten() - @handler = CompositeHandler.new(handles) - @func_map = {} - @type_alias = {} - end - - # Sets the type alias for +alias_type+ as +orig_type+ - def typealias(alias_type, orig_type) - @type_alias[alias_type] = orig_type - end - - # Returns the sizeof +ty+, using Fiddle::Importer.parse_ctype to determine - # the C type and the appropriate Fiddle constant. - def sizeof(ty) - case ty - when String - ty = parse_ctype(ty, type_alias).abs() - case ty - when TYPE_CHAR - return SIZEOF_CHAR - when TYPE_SHORT - return SIZEOF_SHORT - when TYPE_INT - return SIZEOF_INT - when TYPE_LONG - return SIZEOF_LONG - when TYPE_FLOAT - return SIZEOF_FLOAT - when TYPE_DOUBLE - return SIZEOF_DOUBLE - when TYPE_VOIDP - return SIZEOF_VOIDP - when TYPE_CONST_STRING - return SIZEOF_CONST_STRING - when TYPE_BOOL - return SIZEOF_BOOL - else - if defined?(TYPE_LONG_LONG) and - ty == TYPE_LONG_LONG - return SIZEOF_LONG_LONG - else - raise(DLError, "unknown type: #{ty}") - end - end - when Class - if( ty.instance_methods().include?(:to_ptr) ) - return ty.size() - end - end - return Pointer[ty].size() - end - - def parse_bind_options(opts) - h = {} - while( opt = opts.shift() ) - case opt - when :stdcall, :cdecl - h[:call_type] = opt - when :carried, :temp, :temporal, :bind - h[:callback_type] = opt - h[:carrier] = opts.shift() - else - h[opt] = true - end - end - h - end - private :parse_bind_options - - # :stopdoc: - CALL_TYPE_TO_ABI = Hash.new { |h, k| - raise RuntimeError, "unsupported call type: #{k}" - }.merge({ :stdcall => Function.const_defined?(:STDCALL) ? Function::STDCALL : - Function::DEFAULT, - :cdecl => Function::DEFAULT, - nil => Function::DEFAULT - }).freeze - private_constant :CALL_TYPE_TO_ABI - # :startdoc: - - # Creates a global method from the given C +signature+. - def extern(signature, *opts) - symname, ctype, argtype = parse_signature(signature, type_alias) - opt = parse_bind_options(opts) - f = import_function(symname, ctype, argtype, opt[:call_type]) - name = symname.gsub(/@.+/,'') - @func_map[name] = f - # define_method(name){|*args,&block| f.call(*args,&block)} - begin - /^(.+?):(\d+)/ =~ caller.first - file, line = $1, $2.to_i - rescue - file, line = __FILE__, __LINE__+3 - end - module_eval(<<-EOS, file, line) - def #{name}(*args, &block) - @func_map['#{name}'].call(*args,&block) - end - EOS - module_function(name) - f - end - - # Creates a global method from the given C +signature+ using the given - # +opts+ as bind parameters with the given block. - def bind(signature, *opts, &blk) - name, ctype, argtype = parse_signature(signature, type_alias) - h = parse_bind_options(opts) - case h[:callback_type] - when :bind, nil - f = bind_function(name, ctype, argtype, h[:call_type], &blk) - else - raise(RuntimeError, "unknown callback type: #{h[:callback_type]}") - end - @func_map[name] = f - #define_method(name){|*args,&block| f.call(*args,&block)} - begin - /^(.+?):(\d+)/ =~ caller.first - file, line = $1, $2.to_i - rescue - file, line = __FILE__, __LINE__+3 - end - module_eval(<<-EOS, file, line) - def #{name}(*args,&block) - @func_map['#{name}'].call(*args,&block) - end - EOS - module_function(name) - f - end - - # Creates a class to wrap the C struct described by +signature+. - # - # MyStruct = struct ['int i', 'char c'] - def struct(signature) - tys, mems = parse_struct_signature(signature, type_alias) - Fiddle::CStructBuilder.create(CStruct, tys, mems) - end - - # Creates a class to wrap the C union described by +signature+. - # - # MyUnion = union ['int i', 'char c'] - def union(signature) - tys, mems = parse_struct_signature(signature, type_alias) - Fiddle::CStructBuilder.create(CUnion, tys, mems) - end - - # Returns the function mapped to +name+, that was created by either - # Fiddle::Importer.extern or Fiddle::Importer.bind - def [](name) - @func_map[name] - end - - # Creates a class to wrap the C struct with the value +ty+ - # - # See also Fiddle::Importer.struct - def create_value(ty, val=nil) - s = struct([ty + " value"]) - ptr = s.malloc() - if( val ) - ptr.value = val - end - return ptr - end - alias value create_value - - # Returns a new instance of the C struct with the value +ty+ at the +addr+ - # address. - def import_value(ty, addr) - s = struct([ty + " value"]) - ptr = s.new(addr) - return ptr - end - - - # The Fiddle::CompositeHandler instance - # - # Will raise an error if no handlers are open. - def handler - (@handler ||= nil) or raise "call dlload before importing symbols and functions" - end - - # Returns a new Fiddle::Pointer instance at the memory address of the given - # +name+ symbol. - # - # Raises a DLError if the +name+ doesn't exist. - # - # See Fiddle::CompositeHandler.sym and Fiddle::Handle.sym - def import_symbol(name) - addr = handler.sym(name) - if( !addr ) - raise(DLError, "cannot find the symbol: #{name}") - end - Pointer.new(addr) - end - - # Returns a new Fiddle::Function instance at the memory address of the given - # +name+ function. - # - # Raises a DLError if the +name+ doesn't exist. - # - # * +argtype+ is an Array of arguments, passed to the +name+ function. - # * +ctype+ is the return type of the function - # * +call_type+ is the ABI of the function - # - # See also Fiddle:Function.new - # - # See Fiddle::CompositeHandler.sym and Fiddle::Handler.sym - def import_function(name, ctype, argtype, call_type = nil) - addr = handler.sym(name) - if( !addr ) - raise(DLError, "cannot find the function: #{name}()") - end - Function.new(addr, argtype, ctype, CALL_TYPE_TO_ABI[call_type], - name: name) - end - - # Returns a new closure wrapper for the +name+ function. - # - # * +ctype+ is the return type of the function - # * +argtype+ is an Array of arguments, passed to the callback function - # * +call_type+ is the abi of the closure - # * +block+ is passed to the callback - # - # See Fiddle::Closure - def bind_function(name, ctype, argtype, call_type = nil, &block) - abi = CALL_TYPE_TO_ABI[call_type] - closure = Class.new(Fiddle::Closure) { - define_method(:call, block) - }.new(ctype, argtype, abi) - - Function.new(closure, argtype, ctype, abi, name: name) - end - end -end diff --git a/ext/fiddle/lib/fiddle/pack.rb b/ext/fiddle/lib/fiddle/pack.rb deleted file mode 100644 index 81088f402b..0000000000 --- a/ext/fiddle/lib/fiddle/pack.rb +++ /dev/null @@ -1,149 +0,0 @@ -# frozen_string_literal: true -require 'fiddle' - -module Fiddle - module PackInfo # :nodoc: all - ALIGN_MAP = { - TYPE_VOIDP => ALIGN_VOIDP, - TYPE_CHAR => ALIGN_CHAR, - TYPE_SHORT => ALIGN_SHORT, - TYPE_INT => ALIGN_INT, - TYPE_LONG => ALIGN_LONG, - TYPE_FLOAT => ALIGN_FLOAT, - TYPE_DOUBLE => ALIGN_DOUBLE, - TYPE_UCHAR => ALIGN_CHAR, - TYPE_USHORT => ALIGN_SHORT, - TYPE_UINT => ALIGN_INT, - TYPE_ULONG => ALIGN_LONG, - TYPE_BOOL => ALIGN_BOOL, - } - - PACK_MAP = { - TYPE_VOIDP => "L!", - TYPE_CHAR => "c", - TYPE_SHORT => "s!", - TYPE_INT => "i!", - TYPE_LONG => "l!", - TYPE_FLOAT => "f", - TYPE_DOUBLE => "d", - TYPE_UCHAR => "C", - TYPE_USHORT => "S!", - TYPE_UINT => "I!", - TYPE_ULONG => "L!", - } - case SIZEOF_BOOL - when SIZEOF_CHAR - PACK_MAP[TYPE_BOOL] = PACK_MAP[TYPE_UCHAR] - when SIZEOF_SHORT - PACK_MAP[TYPE_BOOL] = PACK_MAP[TYPE_USHORT] - when SIZEOF_INT - PACK_MAP[TYPE_BOOL] = PACK_MAP[TYPE_UINT] - when SIZEOF_LONG - PACK_MAP[TYPE_BOOL] = PACK_MAP[TYPE_ULONG] - end - - SIZE_MAP = { - TYPE_VOIDP => SIZEOF_VOIDP, - TYPE_CHAR => SIZEOF_CHAR, - TYPE_SHORT => SIZEOF_SHORT, - TYPE_INT => SIZEOF_INT, - TYPE_LONG => SIZEOF_LONG, - TYPE_FLOAT => SIZEOF_FLOAT, - TYPE_DOUBLE => SIZEOF_DOUBLE, - TYPE_UCHAR => SIZEOF_CHAR, - TYPE_USHORT => SIZEOF_SHORT, - TYPE_UINT => SIZEOF_INT, - TYPE_ULONG => SIZEOF_LONG, - TYPE_BOOL => SIZEOF_BOOL, - } - if defined?(TYPE_LONG_LONG) - ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_MAP[TYPE_ULONG_LONG] = ALIGN_LONG_LONG - PACK_MAP[TYPE_LONG_LONG] = "q" - PACK_MAP[TYPE_ULONG_LONG] = "Q" - SIZE_MAP[TYPE_LONG_LONG] = SIZE_MAP[TYPE_ULONG_LONG] = SIZEOF_LONG_LONG - PACK_MAP[TYPE_VOIDP] = "Q" if SIZEOF_LONG_LONG == SIZEOF_VOIDP - end - - def align(addr, align) - d = addr % align - if( d == 0 ) - addr - else - addr + (align - d) - end - end - module_function :align - end - - class Packer # :nodoc: all - include PackInfo - - def self.[](*types) - new(types) - end - - def initialize(types) - parse_types(types) - end - - def size() - @size - end - - def pack(ary) - case SIZEOF_VOIDP - when SIZEOF_LONG - ary.pack(@template) - else - if defined?(TYPE_LONG_LONG) and - SIZEOF_VOIDP == SIZEOF_LONG_LONG - ary.pack(@template) - else - raise(RuntimeError, "sizeof(void*)?") - end - end - end - - def unpack(ary) - case SIZEOF_VOIDP - when SIZEOF_LONG - ary.join().unpack(@template) - else - if defined?(TYPE_LONG_LONG) and - SIZEOF_VOIDP == SIZEOF_LONG_LONG - ary.join().unpack(@template) - else - raise(RuntimeError, "sizeof(void*)?") - end - end - end - - private - - def parse_types(types) - @template = "".dup - addr = 0 - types.each{|t| - orig_addr = addr - if( t.is_a?(Array) ) - addr = align(orig_addr, ALIGN_MAP[TYPE_VOIDP]) - else - addr = align(orig_addr, ALIGN_MAP[t]) - end - d = addr - orig_addr - if( d > 0 ) - @template << "x#{d}" - end - if( t.is_a?(Array) ) - @template << (PACK_MAP[t[0]] * t[1]) - addr += (SIZE_MAP[t[0]] * t[1]) - else - @template << PACK_MAP[t] - addr += SIZE_MAP[t] - end - } - addr = align(addr, ALIGN_MAP[TYPE_VOIDP]) - @size = addr - end - end -end diff --git a/ext/fiddle/lib/fiddle/struct.rb b/ext/fiddle/lib/fiddle/struct.rb deleted file mode 100644 index 6d05bbd742..0000000000 --- a/ext/fiddle/lib/fiddle/struct.rb +++ /dev/null @@ -1,539 +0,0 @@ -# frozen_string_literal: true -require 'fiddle' -require 'fiddle/value' -require 'fiddle/pack' - -module Fiddle - # A base class for objects representing a C structure - class CStruct - include Enumerable - - # accessor to Fiddle::CStructEntity - def CStruct.entity_class - CStructEntity - end - - def self.offsetof(name, members, types) # :nodoc: - offset = 0 - worklist = name.split('.') - this_type = self - while search_name = worklist.shift - index = 0 - member_index = members.index(search_name) - - unless member_index - # Possibly a sub-structure - member_index = members.index { |member_name, _| - member_name == search_name - } - return unless member_index - end - - types.each { |type, count = 1| - orig_offset = offset - if type.respond_to?(:entity_class) - align = type.alignment - type_size = type.size - else - align = PackInfo::ALIGN_MAP[type] - type_size = PackInfo::SIZE_MAP[type] - end - - # Unions shouldn't advance the offset - if this_type.entity_class == CUnionEntity - type_size = 0 - end - - offset = PackInfo.align(orig_offset, align) - - if worklist.empty? - return offset if index == member_index - else - if index == member_index - subtype = types[member_index] - members = subtype.members - types = subtype.types - this_type = subtype - break - end - end - - offset += (type_size * count) - index += 1 - } - end - nil - end - - def each - return enum_for(__function__) unless block_given? - - self.class.members.each do |name,| - yield(self[name]) - end - end - - def each_pair - return enum_for(__function__) unless block_given? - - self.class.members.each do |name,| - yield(name, self[name]) - end - end - - def to_h - hash = {} - each_pair do |name, value| - hash[name] = unstruct(value) - end - hash - end - - def replace(another) - if another.nil? - self.class.members.each do |name,| - self[name] = nil - end - elsif another.respond_to?(:each_pair) - another.each_pair do |name, value| - self[name] = value - end - else - another.each do |name, value| - self[name] = value - end - end - self - end - - private - def unstruct(value) - case value - when CStruct - value.to_h - when Array - value.collect do |v| - unstruct(v) - end - else - value - end - end - end - - # A base class for objects representing a C union - class CUnion - # accessor to Fiddle::CUnionEntity - def CUnion.entity_class - CUnionEntity - end - - def self.offsetof(name, members, types) # :nodoc: - 0 - end - end - - # Wrapper for arrays within a struct - class StructArray < Array - include ValueUtil - - def initialize(ptr, type, initial_values) - @ptr = ptr - @type = type - @is_struct = @type.respond_to?(:entity_class) - if @is_struct - super(initial_values) - else - @size = Fiddle::PackInfo::SIZE_MAP[type] - @pack_format = Fiddle::PackInfo::PACK_MAP[type] - super(initial_values.collect { |v| unsigned_value(v, type) }) - end - end - - def to_ptr - @ptr - end - - def []=(index, value) - if index < 0 || index >= size - raise IndexError, 'index %d outside of array bounds 0...%d' % [index, size] - end - - if @is_struct - self[index].replace(value) - else - to_ptr[index * @size, @size] = [value].pack(@pack_format) - super(index, value) - end - end - end - - # Used to construct C classes (CUnion, CStruct, etc) - # - # Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an - # easy-to-use manner. - module CStructBuilder - # Construct a new class given a C: - # * class +klass+ (CUnion, CStruct, or other that provide an - # #entity_class) - # * +types+ (Fiddle::TYPE_INT, Fiddle::TYPE_SIZE_T, etc., see the C types - # constants) - # * corresponding +members+ - # - # Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an - # easy-to-use manner. - # - # Examples: - # - # require 'fiddle/struct' - # require 'fiddle/cparser' - # - # include Fiddle::CParser - # - # types, members = parse_struct_signature(['int i','char c']) - # - # MyStruct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members) - # - # MyStruct.malloc(Fiddle::RUBY_FREE) do |obj| - # ... - # end - # - # obj = MyStruct.malloc(Fiddle::RUBY_FREE) - # begin - # ... - # ensure - # obj.call_free - # end - # - # obj = MyStruct.malloc - # begin - # ... - # ensure - # Fiddle.free obj.to_ptr - # end - # - def create(klass, types, members) - new_class = Class.new(klass){ - define_method(:initialize){|addr, func = nil| - if addr.is_a?(self.class.entity_class) - @entity = addr - else - @entity = self.class.entity_class.new(addr, types, func) - end - @entity.assign_names(members) - } - define_method(:[]) { |*args| @entity.send(:[], *args) } - define_method(:[]=) { |*args| @entity.send(:[]=, *args) } - define_method(:to_ptr){ @entity } - define_method(:to_i){ @entity.to_i } - define_singleton_method(:types) { types } - define_singleton_method(:members) { members } - - # Return the offset of a struct member given its name. - # For example: - # - # MyStruct = struct [ - # "int64_t i", - # "char c", - # ] - # - # MyStruct.offsetof("i") # => 0 - # MyStruct.offsetof("c") # => 8 - # - define_singleton_method(:offsetof) { |name| - klass.offsetof(name, members, types) - } - members.each{|name| - name = name[0] if name.is_a?(Array) # name is a nested struct - next if method_defined?(name) - define_method(name){ @entity[name] } - define_method(name + "="){|val| @entity[name] = val } - } - entity_class = klass.entity_class - alignment = entity_class.alignment(types) - size = entity_class.size(types) - define_singleton_method(:alignment) { alignment } - define_singleton_method(:size) { size } - define_singleton_method(:malloc) do |func=nil, &block| - if block - entity_class.malloc(types, func, size) do |entity| - block.call(new(entity)) - end - else - new(entity_class.malloc(types, func, size)) - end - end - } - return new_class - end - module_function :create - end - - # A pointer to a C structure - class CStructEntity < Fiddle::Pointer - include PackInfo - include ValueUtil - - def CStructEntity.alignment(types) - max = 1 - types.each do |type, count = 1| - if type.respond_to?(:entity_class) - n = type.alignment - else - n = ALIGN_MAP[type] - end - max = n if n > max - end - max - end - - # Allocates a C struct with the +types+ provided. - # - # See Fiddle::Pointer.malloc for memory management issues. - def CStructEntity.malloc(types, func = nil, size = size(types), &block) - if block_given? - super(size, func) do |struct| - struct.set_ctypes types - yield struct - end - else - struct = super(size, func) - struct.set_ctypes types - struct - end - end - - # Returns the offset for the packed sizes for the given +types+. - # - # Fiddle::CStructEntity.size( - # [ Fiddle::TYPE_DOUBLE, - # Fiddle::TYPE_INT, - # Fiddle::TYPE_CHAR, - # Fiddle::TYPE_VOIDP ]) #=> 24 - def CStructEntity.size(types) - offset = 0 - - max_align = types.map { |type, count = 1| - last_offset = offset - - if type.respond_to?(:entity_class) - align = type.alignment - type_size = type.size - else - align = PackInfo::ALIGN_MAP[type] - type_size = PackInfo::SIZE_MAP[type] - end - offset = PackInfo.align(last_offset, align) + - (type_size * count) - - align - }.max - - PackInfo.align(offset, max_align) - end - - # Wraps the C pointer +addr+ as a C struct with the given +types+. - # - # When the instance is garbage collected, the C function +func+ is called. - # - # See also Fiddle::Pointer.new - def initialize(addr, types, func = nil) - if func && addr.is_a?(Pointer) && addr.free - raise ArgumentError, 'free function specified on both underlying struct Pointer and when creating a CStructEntity - who do you want to free this?' - end - set_ctypes(types) - super(addr, @size, func) - end - - # Set the names of the +members+ in this C struct - def assign_names(members) - @members = [] - @nested_structs = {} - members.each_with_index do |member, index| - if member.is_a?(Array) # nested struct - member_name = member[0] - struct_type, struct_count = @ctypes[index] - if struct_count.nil? - struct = struct_type.new(to_i + @offset[index]) - else - structs = struct_count.times.map do |i| - struct_type.new(to_i + @offset[index] + i * struct_type.size) - end - struct = StructArray.new(to_i + @offset[index], - struct_type, - structs) - end - @nested_structs[member_name] = struct - else - member_name = member - end - @members << member_name - end - end - - # Calculates the offsets and sizes for the given +types+ in the struct. - def set_ctypes(types) - @ctypes = types - @offset = [] - offset = 0 - - max_align = types.map { |type, count = 1| - orig_offset = offset - if type.respond_to?(:entity_class) - align = type.alignment - type_size = type.size - else - align = ALIGN_MAP[type] - type_size = SIZE_MAP[type] - end - offset = PackInfo.align(orig_offset, align) - - @offset << offset - - offset += (type_size * count) - - align - }.max - - @size = PackInfo.align(offset, max_align) - end - - # Fetch struct member +name+ if only one argument is specified. If two - # arguments are specified, the first is an offset and the second is a - # length and this method returns the string of +length+ bytes beginning at - # +offset+. - # - # Examples: - # - # my_struct = struct(['int id']).malloc - # my_struct.id = 1 - # my_struct['id'] # => 1 - # my_struct[0, 4] # => "\x01\x00\x00\x00".b - # - def [](*args) - return super(*args) if args.size > 1 - name = args[0] - idx = @members.index(name) - if( idx.nil? ) - raise(ArgumentError, "no such member: #{name}") - end - ty = @ctypes[idx] - if( ty.is_a?(Array) ) - if ty.first.respond_to?(:entity_class) - return @nested_structs[name] - else - r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1]) - end - elsif ty.respond_to?(:entity_class) - return @nested_structs[name] - else - r = super(@offset[idx], SIZE_MAP[ty.abs]) - end - packer = Packer.new([ty]) - val = packer.unpack([r]) - case ty - when Array - case ty[0] - when TYPE_VOIDP - val = val.collect{|v| Pointer.new(v)} - end - when TYPE_VOIDP - val = Pointer.new(val[0]) - else - val = val[0] - end - if( ty.is_a?(Integer) && (ty < 0) ) - return unsigned_value(val, ty) - elsif( ty.is_a?(Array) && (ty[0] < 0) ) - return StructArray.new(self + @offset[idx], ty[0], val) - else - return val - end - end - - # Set struct member +name+, to value +val+. If more arguments are - # specified, writes the string of bytes to the memory at the given - # +offset+ and +length+. - # - # Examples: - # - # my_struct = struct(['int id']).malloc - # my_struct['id'] = 1 - # my_struct[0, 4] = "\x01\x00\x00\x00".b - # my_struct.id # => 1 - # - def []=(*args) - return super(*args) if args.size > 2 - name, val = *args - name = name.to_s if name.is_a?(Symbol) - nested_struct = @nested_structs[name] - if nested_struct - if nested_struct.is_a?(StructArray) - if val.nil? - nested_struct.each do |s| - s.replace(nil) - end - else - val.each_with_index do |v, i| - nested_struct[i] = v - end - end - else - nested_struct.replace(val) - end - return val - end - idx = @members.index(name) - if( idx.nil? ) - raise(ArgumentError, "no such member: #{name}") - end - ty = @ctypes[idx] - packer = Packer.new([ty]) - val = wrap_arg(val, ty, []) - buff = packer.pack([val].flatten()) - super(@offset[idx], buff.size, buff) - if( ty.is_a?(Integer) && (ty < 0) ) - return unsigned_value(val, ty) - elsif( ty.is_a?(Array) && (ty[0] < 0) ) - return val.collect{|v| unsigned_value(v,ty[0])} - else - return val - end - end - - undef_method :size= - def to_s() # :nodoc: - super(@size) - end - end - - # A pointer to a C union - class CUnionEntity < CStructEntity - include PackInfo - - # Returns the size needed for the union with the given +types+. - # - # Fiddle::CUnionEntity.size( - # [ Fiddle::TYPE_DOUBLE, - # Fiddle::TYPE_INT, - # Fiddle::TYPE_CHAR, - # Fiddle::TYPE_VOIDP ]) #=> 8 - def CUnionEntity.size(types) - types.map { |type, count = 1| - if type.respond_to?(:entity_class) - type.size * count - else - PackInfo::SIZE_MAP[type] * count - end - }.max - end - - # Calculate the necessary offset and for each union member with the given - # +types+ - def set_ctypes(types) - @ctypes = types - @offset = Array.new(types.length, 0) - @size = self.class.size types - end - end -end diff --git a/ext/fiddle/lib/fiddle/types.rb b/ext/fiddle/lib/fiddle/types.rb deleted file mode 100644 index 7baf31ec9e..0000000000 --- a/ext/fiddle/lib/fiddle/types.rb +++ /dev/null @@ -1,73 +0,0 @@ -# frozen_string_literal: true -module Fiddle - # Adds Windows type aliases to the including class for use with - # Fiddle::Importer. - # - # The aliases added are: - # * ATOM - # * BOOL - # * BYTE - # * DWORD - # * DWORD32 - # * DWORD64 - # * HANDLE - # * HDC - # * HINSTANCE - # * HWND - # * LPCSTR - # * LPSTR - # * PBYTE - # * PDWORD - # * PHANDLE - # * PVOID - # * PWORD - # * UCHAR - # * UINT - # * ULONG - # * WORD - module Win32Types - def included(m) # :nodoc: - # https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types - m.module_eval{ - typealias "ATOM", "WORD" - typealias "BOOL", "int" - typealias "BYTE", "unsigned char" - typealias "DWORD", "unsigned long" - typealias "DWORD32", "uint32_t" - typealias "DWORD64", "uint64_t" - typealias "HANDLE", "PVOID" - typealias "HDC", "HANDLE" - typealias "HINSTANCE", "HANDLE" - typealias "HWND", "HANDLE" - typealias "LPCSTR", "const char *" - typealias "LPSTR", "char *" - typealias "PBYTE", "BYTE *" - typealias "PDWORD", "DWORD *" - typealias "PHANDLE", "HANDLE *" - typealias "PVOID", "void *" - typealias "PWORD", "WORD *" - typealias "UCHAR", "unsigned char" - typealias "UINT", "unsigned int" - typealias "ULONG", "unsigned long" - typealias "WORD", "unsigned short" - } - end - module_function :included - end - - # Adds basic type aliases to the including class for use with Fiddle::Importer. - # - # The aliases added are +uint+ and +u_int+ (<tt>unsigned int</tt>) and - # +ulong+ and +u_long+ (<tt>unsigned long</tt>) - module BasicTypes - def included(m) # :nodoc: - m.module_eval{ - typealias "uint", "unsigned int" - typealias "u_int", "unsigned int" - typealias "ulong", "unsigned long" - typealias "u_long", "unsigned long" - } - end - module_function :included - end -end diff --git a/ext/fiddle/lib/fiddle/value.rb b/ext/fiddle/lib/fiddle/value.rb deleted file mode 100644 index 5f0b2e951e..0000000000 --- a/ext/fiddle/lib/fiddle/value.rb +++ /dev/null @@ -1,120 +0,0 @@ -# frozen_string_literal: true -require 'fiddle' - -module Fiddle - module ValueUtil #:nodoc: all - def unsigned_value(val, ty) - case ty.abs - when TYPE_CHAR - [val].pack("c").unpack1("C") - when TYPE_SHORT - [val].pack("s!").unpack1("S!") - when TYPE_INT - [val].pack("i!").unpack1("I!") - when TYPE_LONG - [val].pack("l!").unpack1("L!") - else - if defined?(TYPE_LONG_LONG) and - ty.abs == TYPE_LONG_LONG - [val].pack("q").unpack1("Q") - else - val - end - end - end - - def signed_value(val, ty) - case ty.abs - when TYPE_CHAR - [val].pack("C").unpack1("c") - when TYPE_SHORT - [val].pack("S!").unpack1("s!") - when TYPE_INT - [val].pack("I!").unpack1("i!") - when TYPE_LONG - [val].pack("L!").unpack1("l!") - else - if defined?(TYPE_LONG_LONG) and - ty.abs == TYPE_LONG_LONG - [val].pack("Q").unpack1("q") - else - val - end - end - end - - def wrap_args(args, tys, funcs, &block) - result = [] - tys ||= [] - args.each_with_index{|arg, idx| - result.push(wrap_arg(arg, tys[idx], funcs, &block)) - } - result - end - - def wrap_arg(arg, ty, funcs = [], &block) - funcs ||= [] - case arg - when nil - return 0 - when Pointer - return arg.to_i - when IO - case ty - when TYPE_VOIDP - return Pointer[arg].to_i - else - return arg.to_i - end - when Function - if( block ) - arg.bind_at_call(&block) - funcs.push(arg) - elsif !arg.bound? - raise(RuntimeError, "block must be given.") - end - return arg.to_i - when String - if( ty.is_a?(Array) ) - return arg.unpack('C*') - else - case SIZEOF_VOIDP - when SIZEOF_LONG - return [arg].pack("p").unpack1("l!") - else - if defined?(SIZEOF_LONG_LONG) and - SIZEOF_VOIDP == SIZEOF_LONG_LONG - return [arg].pack("p").unpack1("q") - else - raise(RuntimeError, "sizeof(void*)?") - end - end - end - when Float, Integer - return arg - when Array - if( ty.is_a?(Array) ) # used only by struct - case ty[0] - when TYPE_VOIDP - return arg.collect{|v| Integer(v)} - when TYPE_CHAR - if( arg.is_a?(String) ) - return val.unpack('C*') - end - end - end - return arg - else - if( arg.respond_to?(:to_ptr) ) - return arg.to_ptr.to_i - else - begin - return Integer(arg) - rescue - raise(ArgumentError, "unknown argument type: #{arg.class}") - end - end - end - end - end -end diff --git a/ext/fiddle/lib/fiddle/version.rb b/ext/fiddle/lib/fiddle/version.rb deleted file mode 100644 index 6c5109dca8..0000000000 --- a/ext/fiddle/lib/fiddle/version.rb +++ /dev/null @@ -1,3 +0,0 @@ -module Fiddle - VERSION = "1.1.3" -end diff --git a/ext/fiddle/memory_view.c b/ext/fiddle/memory_view.c deleted file mode 100644 index fa66fc2c7b..0000000000 --- a/ext/fiddle/memory_view.c +++ /dev/null @@ -1,321 +0,0 @@ -#include <fiddle.h> - -#ifdef HAVE_RUBY_MEMORY_VIEW_H - -#include <stdbool.h> -#include <ruby/ruby.h> -#include <ruby/encoding.h> -#include <ruby/memory_view.h> - -#if SIZEOF_INTPTR_T == SIZEOF_LONG_LONG -# define INTPTR2NUM LL2NUM -# define UINTPTR2NUM ULL2NUM -#elif SIZEOF_INTPTR_T == SIZEOF_LONG -# define INTPTR2NUM LONG2NUM -# define UINTPTR2NUM ULONG2NUM -#else -# define INTPTR2NUM INT2NUM -# define UINTPTR2NUM UINT2NUM -#endif - -VALUE rb_cMemoryView = Qnil; - -struct memview_data { - rb_memory_view_t view; - rb_memory_view_item_component_t *members; - size_t n_members; -}; - -static void -fiddle_memview_mark(void *ptr) -{ - const struct memview_data *data = ptr; - rb_gc_mark(data->view.obj); -} - -static void -fiddle_memview_release(struct memview_data *data) -{ - if (NIL_P(data->view.obj)) return; - - rb_memory_view_release(&data->view); - data->view.obj = Qnil; - data->view.byte_size = 0; - if (data->members) { - xfree(data->members); - data->members = NULL; - data->n_members = 0; - } -} - -static void -fiddle_memview_free(void *ptr) -{ - struct memview_data *data = ptr; - fiddle_memview_release(data); - xfree(ptr); -} - -static size_t -fiddle_memview_memsize(const void *ptr) -{ - const struct memview_data *data = ptr; - return sizeof(*data) + sizeof(rb_memory_view_item_component_t)*data->n_members + (size_t)data->view.byte_size; -} - -static const rb_data_type_t fiddle_memview_data_type = { - "fiddle/memory_view", - {fiddle_memview_mark, fiddle_memview_free, fiddle_memview_memsize,}, -}; - -static VALUE -rb_fiddle_memview_s_allocate(VALUE klass) -{ - struct memview_data *data; - VALUE obj = TypedData_Make_Struct(klass, struct memview_data, &fiddle_memview_data_type, data); - data->view.obj = Qnil; - data->view.byte_size = 0; - data->members = NULL; - data->n_members = 0; - return obj; -} - -static VALUE -rb_fiddle_memview_release(VALUE obj) -{ - struct memview_data *data; - TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); - - if (NIL_P(data->view.obj)) return Qnil; - fiddle_memview_release(data); - return Qnil; -} - -static VALUE -rb_fiddle_memview_s_export(VALUE klass, VALUE target) -{ - ID id_new; - CONST_ID(id_new, "new"); - VALUE memview = rb_funcall(klass, id_new, 1, target); - return rb_ensure(rb_yield, memview, rb_fiddle_memview_release, memview); -} - -static VALUE -rb_fiddle_memview_initialize(VALUE obj, VALUE target) -{ - struct memview_data *data; - TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); - - if (!rb_memory_view_get(target, &data->view, 0)) { - data->view.obj = Qnil; - rb_raise(rb_eArgError, "Unable to get a memory view from %+"PRIsVALUE, target); - } - - return Qnil; -} - -static VALUE -rb_fiddle_memview_get_obj(VALUE obj) -{ - struct memview_data *data; - TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); - - return data->view.obj; -} - -static VALUE -rb_fiddle_memview_get_byte_size(VALUE obj) -{ - struct memview_data *data; - TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); - - if (NIL_P(data->view.obj)) return Qnil; - return SSIZET2NUM(data->view.byte_size); -} - -static VALUE -rb_fiddle_memview_get_readonly(VALUE obj) -{ - struct memview_data *data; - TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); - - if (NIL_P(data->view.obj)) return Qnil; - return data->view.readonly ? Qtrue : Qfalse; -} - -static VALUE -rb_fiddle_memview_get_format(VALUE obj) -{ - struct memview_data *data; - TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); - - if (NIL_P(data->view.obj)) return Qnil; - return data->view.format == NULL ? Qnil : rb_str_new_cstr(data->view.format); -} - -static VALUE -rb_fiddle_memview_get_item_size(VALUE obj) -{ - struct memview_data *data; - TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); - - if (NIL_P(data->view.obj)) return Qnil; - return SSIZET2NUM(data->view.item_size); -} - -static VALUE -rb_fiddle_memview_get_ndim(VALUE obj) -{ - struct memview_data *data; - TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); - - if (NIL_P(data->view.obj)) return Qnil; - return SSIZET2NUM(data->view.ndim); -} - -static VALUE -rb_fiddle_memview_get_shape(VALUE obj) -{ - struct memview_data *data; - TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); - - if (NIL_P(data->view.obj)) return Qnil; - if (data->view.shape == NULL) return Qnil; - - const ssize_t ndim = data->view.ndim; - VALUE shape = rb_ary_new_capa(ndim); - ssize_t i; - for (i = 0; i < ndim; ++i) { - rb_ary_push(shape, SSIZET2NUM(data->view.shape[i])); - } - return shape; -} - -static VALUE -rb_fiddle_memview_get_strides(VALUE obj) -{ - struct memview_data *data; - TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); - - if (NIL_P(data->view.obj)) return Qnil; - if (data->view.strides == NULL) return Qnil; - - const ssize_t ndim = data->view.ndim; - VALUE strides = rb_ary_new_capa(ndim); - ssize_t i; - for (i = 0; i < ndim; ++i) { - rb_ary_push(strides, SSIZET2NUM(data->view.strides[i])); - } - return strides; -} - -static VALUE -rb_fiddle_memview_get_sub_offsets(VALUE obj) -{ - struct memview_data *data; - TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); - - if (NIL_P(data->view.obj)) return Qnil; - if (data->view.sub_offsets == NULL) return Qnil; - - const ssize_t ndim = data->view.ndim; - VALUE sub_offsets = rb_ary_new_capa(ndim); - ssize_t i; - for (i = 0; i < ndim; ++i) { - rb_ary_push(sub_offsets, SSIZET2NUM(data->view.sub_offsets[i])); - } - return sub_offsets; -} - -static VALUE -rb_fiddle_memview_aref(int argc, VALUE *argv, VALUE obj) -{ - struct memview_data *data; - TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); - - if (NIL_P(data->view.obj)) return Qnil; - - const ssize_t ndim = data->view.ndim; - if (argc != ndim) { - rb_raise(rb_eIndexError, "wrong number of index (%d for %"PRIdSIZE")", argc, ndim); - } - - VALUE indices_v = 0; - ssize_t *indices = ALLOCV_N(ssize_t, indices_v, ndim); - - ssize_t i; - for (i = 0; i < ndim; ++i) { - ssize_t x = NUM2SSIZET(argv[i]); - indices[i] = x; - } - - uint8_t *ptr = rb_memory_view_get_item_pointer(&data->view, indices); - ALLOCV_END(indices_v); - - if (data->view.format == NULL) { - return INT2FIX(*ptr); - } - - if (!data->members) { - const char *err; - if (rb_memory_view_parse_item_format(data->view.format, &data->members, &data->n_members, &err) < 0) { - rb_raise(rb_eRuntimeError, "Unable to recognize item format at %"PRIdSIZE" in \"%s\"", - err - data->view.format, data->view.format); - } - } - - return rb_memory_view_extract_item_members(ptr, data->members, data->n_members); -} - -static VALUE -rb_fiddle_memview_to_s(VALUE self) -{ - struct memview_data *data; - const char *raw_data; - long byte_size; - VALUE string; - - TypedData_Get_Struct(self, - struct memview_data, - &fiddle_memview_data_type, - data); - - if (NIL_P(data->view.obj)) { - raw_data = NULL; - byte_size = 0; - } else { - raw_data = data->view.data; - byte_size = data->view.byte_size; - } - - string = rb_enc_str_new_static(raw_data, byte_size, rb_ascii8bit_encoding()); - { - ID id_memory_view; - CONST_ID(id_memory_view, "memory_view"); - rb_ivar_set(string, id_memory_view, self); - } - return rb_obj_freeze(string); -} - -void -Init_fiddle_memory_view(void) -{ - rb_cMemoryView = rb_define_class_under(mFiddle, "MemoryView", rb_cObject); - rb_define_alloc_func(rb_cMemoryView, rb_fiddle_memview_s_allocate); - rb_define_singleton_method(rb_cMemoryView, "export", rb_fiddle_memview_s_export, 1); - rb_define_method(rb_cMemoryView, "initialize", rb_fiddle_memview_initialize, 1); - rb_define_method(rb_cMemoryView, "release", rb_fiddle_memview_release, 0); - rb_define_method(rb_cMemoryView, "obj", rb_fiddle_memview_get_obj, 0); - rb_define_method(rb_cMemoryView, "byte_size", rb_fiddle_memview_get_byte_size, 0); - rb_define_method(rb_cMemoryView, "readonly?", rb_fiddle_memview_get_readonly, 0); - rb_define_method(rb_cMemoryView, "format", rb_fiddle_memview_get_format, 0); - rb_define_method(rb_cMemoryView, "item_size", rb_fiddle_memview_get_item_size, 0); - rb_define_method(rb_cMemoryView, "ndim", rb_fiddle_memview_get_ndim, 0); - rb_define_method(rb_cMemoryView, "shape", rb_fiddle_memview_get_shape, 0); - rb_define_method(rb_cMemoryView, "strides", rb_fiddle_memview_get_strides, 0); - rb_define_method(rb_cMemoryView, "sub_offsets", rb_fiddle_memview_get_sub_offsets, 0); - rb_define_method(rb_cMemoryView, "[]", rb_fiddle_memview_aref, -1); - rb_define_method(rb_cMemoryView, "to_s", rb_fiddle_memview_to_s, 0); -} - -#endif /* HAVE_RUBY_MEMORY_VIEW_H */ diff --git a/ext/fiddle/pinned.c b/ext/fiddle/pinned.c deleted file mode 100644 index 019a3020e2..0000000000 --- a/ext/fiddle/pinned.c +++ /dev/null @@ -1,123 +0,0 @@ -#include <fiddle.h> - -VALUE rb_cPinned; -VALUE rb_eFiddleClearedReferenceError; - -struct pinned_data { - VALUE ptr; -}; - -static void -pinned_mark(void *ptr) -{ - struct pinned_data *data = (struct pinned_data*)ptr; - /* Ensure reference is pinned */ - if (data->ptr) { - rb_gc_mark(data->ptr); - } -} - -static size_t -pinned_memsize(const void *ptr) -{ - return sizeof(struct pinned_data); -} - -static const rb_data_type_t pinned_data_type = { - "fiddle/pinned", - {pinned_mark, xfree, pinned_memsize, }, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED -}; - -static VALUE -allocate(VALUE klass) -{ - struct pinned_data *data; - VALUE obj = TypedData_Make_Struct(klass, struct pinned_data, &pinned_data_type, data); - data->ptr = 0; - return obj; -} - -/* - * call-seq: - * Fiddle::Pinned.new(object) => pinned_object - * - * Create a new pinned object reference. The Fiddle::Pinned instance will - * prevent the GC from moving +object+. - */ -static VALUE -initialize(VALUE self, VALUE ref) -{ - struct pinned_data *data; - TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data); - RB_OBJ_WRITE(self, &data->ptr, ref); - return self; -} - -/* - * call-seq: ref - * - * Return the object that this pinned instance references. - */ -static VALUE -ref(VALUE self) -{ - struct pinned_data *data; - TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data); - if (data->ptr) { - return data->ptr; - } else { - rb_raise(rb_eFiddleClearedReferenceError, "`ref` called on a cleared object"); - } -} - -/* - * call-seq: clear - * - * Clear the reference to the object this is pinning. - */ -static VALUE -clear(VALUE self) -{ - struct pinned_data *data; - TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data); - data->ptr = 0; - return self; -} - -/* - * call-seq: cleared? - * - * Returns true if the reference has been cleared, otherwise returns false. - */ -static VALUE -cleared_p(VALUE self) -{ - struct pinned_data *data; - TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data); - if (data->ptr) { - return Qfalse; - } else { - return Qtrue; - } -} - -extern VALUE rb_eFiddleError; - -void -Init_fiddle_pinned(void) -{ - rb_cPinned = rb_define_class_under(mFiddle, "Pinned", rb_cObject); - rb_define_alloc_func(rb_cPinned, allocate); - rb_define_method(rb_cPinned, "initialize", initialize, 1); - rb_define_method(rb_cPinned, "ref", ref, 0); - rb_define_method(rb_cPinned, "clear", clear, 0); - rb_define_method(rb_cPinned, "cleared?", cleared_p, 0); - - /* - * Document-class: Fiddle::ClearedReferenceError - * - * Cleared reference exception - */ - rb_eFiddleClearedReferenceError = rb_define_class_under(mFiddle, "ClearedReferenceError", rb_eFiddleError); -} diff --git a/ext/fiddle/pointer.c b/ext/fiddle/pointer.c deleted file mode 100644 index 1b7d7a69f6..0000000000 --- a/ext/fiddle/pointer.c +++ /dev/null @@ -1,887 +0,0 @@ -/* -*- C -*- - * $Id$ - */ - -#include <stdbool.h> -#include <ruby/ruby.h> -#include <ruby/io.h> - -#include <ctype.h> -#include <fiddle.h> - -#ifdef HAVE_RUBY_MEMORY_VIEW_H -# include <ruby/memory_view.h> -#endif - -#ifdef PRIsVALUE -# define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj) -# define RB_OBJ_STRING(obj) (obj) -#else -# define PRIsVALUE "s" -# define RB_OBJ_CLASSNAME(obj) rb_obj_classname(obj) -# define RB_OBJ_STRING(obj) StringValueCStr(obj) -#endif - -VALUE rb_cPointer; - -typedef rb_fiddle_freefunc_t freefunc_t; - -struct ptr_data { - void *ptr; - long size; - freefunc_t free; - bool freed; - VALUE wrap[2]; -}; - -#define RPTR_DATA(obj) ((struct ptr_data *)(DATA_PTR(obj))) - -static inline freefunc_t -get_freefunc(VALUE func, volatile VALUE *wrap) -{ - VALUE addrnum; - if (NIL_P(func)) { - *wrap = 0; - return NULL; - } - addrnum = rb_Integer(func); - *wrap = (addrnum != func) ? func : 0; - return (freefunc_t)(VALUE)NUM2PTR(addrnum); -} - -static ID id_to_ptr; - -static void -fiddle_ptr_mark(void *ptr) -{ - struct ptr_data *data = ptr; - if (data->wrap[0]) { - rb_gc_mark(data->wrap[0]); - } - if (data->wrap[1]) { - rb_gc_mark(data->wrap[1]); - } -} - -static void -fiddle_ptr_free_ptr(void *ptr) -{ - struct ptr_data *data = ptr; - if (data->ptr && data->free && !data->freed) { - data->freed = true; - (*(data->free))(data->ptr); - } -} - -static void -fiddle_ptr_free(void *ptr) -{ - fiddle_ptr_free_ptr(ptr); - xfree(ptr); -} - -static size_t -fiddle_ptr_memsize(const void *ptr) -{ - const struct ptr_data *data = ptr; - return sizeof(*data) + data->size; -} - -static const rb_data_type_t fiddle_ptr_data_type = { - .wrap_struct_name = "fiddle/pointer", - .function = { - .dmark = fiddle_ptr_mark, - .dfree = fiddle_ptr_free, - .dsize = fiddle_ptr_memsize, - }, - .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED -}; - -#ifdef HAVE_RUBY_MEMORY_VIEW_H -static struct ptr_data * -fiddle_ptr_check_memory_view(VALUE obj) -{ - struct ptr_data *data; - TypedData_Get_Struct(obj, struct ptr_data, &fiddle_ptr_data_type, data); - if (data->ptr == NULL || data->size == 0) return NULL; - return data; -} - -static bool -fiddle_ptr_memory_view_available_p(VALUE obj) -{ - return fiddle_ptr_check_memory_view(obj) != NULL; -} - -static bool -fiddle_ptr_get_memory_view(VALUE obj, rb_memory_view_t *view, int flags) -{ - struct ptr_data *data = fiddle_ptr_check_memory_view(obj); - rb_memory_view_init_as_byte_array(view, obj, data->ptr, data->size, true); - - return true; -} - -static const rb_memory_view_entry_t fiddle_ptr_memory_view_entry = { - fiddle_ptr_get_memory_view, - NULL, - fiddle_ptr_memory_view_available_p -}; -#endif - -static VALUE -rb_fiddle_ptr_new2(VALUE klass, void *ptr, long size, freefunc_t func, VALUE wrap0, VALUE wrap1) -{ - struct ptr_data *data; - VALUE val; - - val = TypedData_Make_Struct(klass, struct ptr_data, &fiddle_ptr_data_type, data); - data->ptr = ptr; - data->free = func; - data->freed = false; - data->size = size; - RB_OBJ_WRITE(val, &data->wrap[0], wrap0); - RB_OBJ_WRITE(val, &data->wrap[1], wrap1); - - return val; -} - -VALUE -rb_fiddle_ptr_new_wrap(void *ptr, long size, freefunc_t func, VALUE wrap0, VALUE wrap1) -{ - return rb_fiddle_ptr_new2(rb_cPointer, ptr, size, func, wrap0, wrap1); -} - -static VALUE -rb_fiddle_ptr_new(void *ptr, long size, freefunc_t func) -{ - return rb_fiddle_ptr_new2(rb_cPointer, ptr, size, func, 0, 0); -} - -static VALUE -rb_fiddle_ptr_malloc(VALUE klass, long size, freefunc_t func) -{ - void *ptr; - - ptr = ruby_xmalloc((size_t)size); - memset(ptr,0,(size_t)size); - return rb_fiddle_ptr_new2(klass, ptr, size, func, 0, 0); -} - -static void * -rb_fiddle_ptr2cptr(VALUE val) -{ - struct ptr_data *data; - void *ptr; - - if (rb_obj_is_kind_of(val, rb_cPointer)) { - TypedData_Get_Struct(val, struct ptr_data, &fiddle_ptr_data_type, data); - ptr = data->ptr; - } - else if (val == Qnil) { - ptr = NULL; - } - else{ - rb_raise(rb_eTypeError, "Fiddle::Pointer was expected"); - } - - return ptr; -} - -static VALUE -rb_fiddle_ptr_s_allocate(VALUE klass) -{ - VALUE obj; - struct ptr_data *data; - - obj = TypedData_Make_Struct(klass, struct ptr_data, &fiddle_ptr_data_type, data); - data->ptr = 0; - data->size = 0; - data->free = 0; - data->freed = false; - - return obj; -} - -/* - * call-seq: - * Fiddle::Pointer.new(address) => fiddle_cptr - * new(address, size) => fiddle_cptr - * new(address, size, freefunc) => fiddle_cptr - * - * Create a new pointer to +address+ with an optional +size+ and +freefunc+. - * - * +freefunc+ will be called when the instance is garbage collected. - */ -static VALUE -rb_fiddle_ptr_initialize(int argc, VALUE argv[], VALUE self) -{ - VALUE ptr, sym, size, wrap = 0, funcwrap = 0; - struct ptr_data *data; - void *p = NULL; - freefunc_t f = NULL; - long s = 0; - - if (rb_scan_args(argc, argv, "12", &ptr, &size, &sym) >= 1) { - VALUE addrnum = rb_Integer(ptr); - if (addrnum != ptr) wrap = ptr; - p = NUM2PTR(addrnum); - } - if (argc >= 2) { - s = NUM2LONG(size); - } - if (argc >= 3) { - f = get_freefunc(sym, &funcwrap); - } - - if (p) { - TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); - if (data->ptr && data->free) { - /* Free previous memory. Use of inappropriate initialize may cause SEGV. */ - (*(data->free))(data->ptr); - } - RB_OBJ_WRITE(self, &data->wrap[0], wrap); - RB_OBJ_WRITE(self, &data->wrap[1], funcwrap); - data->ptr = p; - data->size = s; - data->free = f; - } - - return Qnil; -} - -static VALUE -rb_fiddle_ptr_call_free(VALUE self); - -/* - * call-seq: - * Fiddle::Pointer.malloc(size, freefunc = nil) => fiddle pointer instance - * Fiddle::Pointer.malloc(size, freefunc) { |pointer| ... } => ... - * - * == Examples - * - * # Automatically freeing the pointer when the block is exited - recommended - * Fiddle::Pointer.malloc(size, Fiddle::RUBY_FREE) do |pointer| - * ... - * end - * - * # Manually freeing but relying on the garbage collector otherwise - * pointer = Fiddle::Pointer.malloc(size, Fiddle::RUBY_FREE) - * ... - * pointer.call_free - * - * # Relying on the garbage collector - may lead to unlimited memory allocated before freeing any, but safe - * pointer = Fiddle::Pointer.malloc(size, Fiddle::RUBY_FREE) - * ... - * - * # Only manually freeing - * pointer = Fiddle::Pointer.malloc(size) - * begin - * ... - * ensure - * Fiddle.free pointer - * end - * - * # No free function and no call to free - the native memory will leak if the pointer is garbage collected - * pointer = Fiddle::Pointer.malloc(size) - * ... - * - * Allocate +size+ bytes of memory and associate it with an optional - * +freefunc+. - * - * If a block is supplied, the pointer will be yielded to the block instead of - * being returned, and the return value of the block will be returned. A - * +freefunc+ must be supplied if a block is. - * - * If a +freefunc+ is supplied it will be called once, when the pointer is - * garbage collected or when the block is left if a block is supplied or - * when the user calls +call_free+, whichever happens first. +freefunc+ must be - * an address pointing to a function or an instance of +Fiddle::Function+. - */ -static VALUE -rb_fiddle_ptr_s_malloc(int argc, VALUE argv[], VALUE klass) -{ - VALUE size, sym, obj, wrap = 0; - long s; - freefunc_t f; - - switch (rb_scan_args(argc, argv, "11", &size, &sym)) { - case 1: - s = NUM2LONG(size); - f = NULL; - break; - case 2: - s = NUM2LONG(size); - f = get_freefunc(sym, &wrap); - break; - default: - rb_bug("rb_fiddle_ptr_s_malloc"); - } - - obj = rb_fiddle_ptr_malloc(klass, s,f); - if (wrap) RB_OBJ_WRITE(obj, &RPTR_DATA(obj)->wrap[1], wrap); - - if (rb_block_given_p()) { - if (!f) { - rb_raise(rb_eArgError, "a free function must be supplied to Fiddle::Pointer.malloc when it is called with a block"); - } - return rb_ensure(rb_yield, obj, rb_fiddle_ptr_call_free, obj); - } else { - return obj; - } -} - -/* - * call-seq: to_i - * - * Returns the integer memory location of this pointer. - */ -static VALUE -rb_fiddle_ptr_to_i(VALUE self) -{ - struct ptr_data *data; - - TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); - return PTR2NUM(data->ptr); -} - -/* - * call-seq: to_value - * - * Cast this pointer to a ruby object. - */ -static VALUE -rb_fiddle_ptr_to_value(VALUE self) -{ - struct ptr_data *data; - TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); - return (VALUE)(data->ptr); -} - -/* - * call-seq: ptr - * - * Returns a new Fiddle::Pointer instance that is a dereferenced pointer for - * this pointer. - * - * Analogous to the star operator in C. - */ -static VALUE -rb_fiddle_ptr_ptr(VALUE self) -{ - struct ptr_data *data; - - TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); - return rb_fiddle_ptr_new(*((void**)(data->ptr)),0,0); -} - -/* - * call-seq: ref - * - * Returns a new Fiddle::Pointer instance that is a reference pointer for this - * pointer. - * - * Analogous to the ampersand operator in C. - */ -static VALUE -rb_fiddle_ptr_ref(VALUE self) -{ - struct ptr_data *data; - - TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); - return rb_fiddle_ptr_new(&(data->ptr),0,0); -} - -/* - * call-seq: null? - * - * Returns +true+ if this is a null pointer. - */ -static VALUE -rb_fiddle_ptr_null_p(VALUE self) -{ - struct ptr_data *data; - - TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); - return data->ptr ? Qfalse : Qtrue; -} - -/* - * call-seq: free=(function) - * - * Set the free function for this pointer to +function+ in the given - * Fiddle::Function. - */ -static VALUE -rb_fiddle_ptr_free_set(VALUE self, VALUE val) -{ - struct ptr_data *data; - - TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); - data->free = get_freefunc(val, &data->wrap[1]); - - return Qnil; -} - -/* - * call-seq: free => Fiddle::Function - * - * Get the free function for this pointer. - * - * Returns a new instance of Fiddle::Function. - * - * See Fiddle::Function.new - */ -static VALUE -rb_fiddle_ptr_free_get(VALUE self) -{ - struct ptr_data *pdata; - VALUE address; - VALUE arg_types; - VALUE ret_type; - - TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, pdata); - - if (!pdata->free) - return Qnil; - - address = PTR2NUM(pdata->free); - ret_type = INT2NUM(TYPE_VOID); - arg_types = rb_ary_new(); - rb_ary_push(arg_types, INT2NUM(TYPE_VOIDP)); - - return rb_fiddle_new_function(address, arg_types, ret_type); -} - -/* - * call-seq: call_free => nil - * - * Call the free function for this pointer. Calling more than once will do - * nothing. Does nothing if there is no free function attached. - */ -static VALUE -rb_fiddle_ptr_call_free(VALUE self) -{ - struct ptr_data *pdata; - TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, pdata); - fiddle_ptr_free_ptr(pdata); - return Qnil; -} - -/* - * call-seq: freed? => bool - * - * Returns if the free function for this pointer has been called. - */ -static VALUE -rb_fiddle_ptr_freed_p(VALUE self) -{ - struct ptr_data *pdata; - TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, pdata); - return pdata->freed ? Qtrue : Qfalse; -} - -/* - * call-seq: - * - * ptr.to_s => string - * ptr.to_s(len) => string - * - * Returns the pointer contents as a string. - * - * When called with no arguments, this method will return the contents until - * the first NULL byte. - * - * When called with +len+, a string of +len+ bytes will be returned. - * - * See to_str - */ -static VALUE -rb_fiddle_ptr_to_s(int argc, VALUE argv[], VALUE self) -{ - struct ptr_data *data; - VALUE arg1, val; - int len; - - TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); - switch (rb_scan_args(argc, argv, "01", &arg1)) { - case 0: - val = rb_str_new2((char*)(data->ptr)); - break; - case 1: - len = NUM2INT(arg1); - val = rb_str_new((char*)(data->ptr), len); - break; - default: - rb_bug("rb_fiddle_ptr_to_s"); - } - - return val; -} - -/* - * call-seq: - * - * ptr.to_str => string - * ptr.to_str(len) => string - * - * Returns the pointer contents as a string. - * - * When called with no arguments, this method will return the contents with the - * length of this pointer's +size+. - * - * When called with +len+, a string of +len+ bytes will be returned. - * - * See to_s - */ -static VALUE -rb_fiddle_ptr_to_str(int argc, VALUE argv[], VALUE self) -{ - struct ptr_data *data; - VALUE arg1, val; - int len; - - TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); - switch (rb_scan_args(argc, argv, "01", &arg1)) { - case 0: - val = rb_str_new((char*)(data->ptr),data->size); - break; - case 1: - len = NUM2INT(arg1); - val = rb_str_new((char*)(data->ptr), len); - break; - default: - rb_bug("rb_fiddle_ptr_to_str"); - } - - return val; -} - -/* - * call-seq: inspect - * - * Returns a string formatted with an easily readable representation of the - * internal state of the pointer. - */ -static VALUE -rb_fiddle_ptr_inspect(VALUE self) -{ - struct ptr_data *data; - - TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); - return rb_sprintf("#<%"PRIsVALUE":%p ptr=%p size=%ld free=%p>", - RB_OBJ_CLASSNAME(self), (void *)data, data->ptr, data->size, (void *)data->free); -} - -/* - * call-seq: - * ptr == other => true or false - * ptr.eql?(other) => true or false - * - * Returns true if +other+ wraps the same pointer, otherwise returns - * false. - */ -static VALUE -rb_fiddle_ptr_eql(VALUE self, VALUE other) -{ - void *ptr1, *ptr2; - - if(!rb_obj_is_kind_of(other, rb_cPointer)) return Qfalse; - - ptr1 = rb_fiddle_ptr2cptr(self); - ptr2 = rb_fiddle_ptr2cptr(other); - - return ptr1 == ptr2 ? Qtrue : Qfalse; -} - -/* - * call-seq: - * ptr <=> other => -1, 0, 1, or nil - * - * Returns -1 if less than, 0 if equal to, 1 if greater than +other+. - * - * Returns nil if +ptr+ cannot be compared to +other+. - */ -static VALUE -rb_fiddle_ptr_cmp(VALUE self, VALUE other) -{ - void *ptr1, *ptr2; - SIGNED_VALUE diff; - - if(!rb_obj_is_kind_of(other, rb_cPointer)) return Qnil; - - ptr1 = rb_fiddle_ptr2cptr(self); - ptr2 = rb_fiddle_ptr2cptr(other); - diff = (SIGNED_VALUE)ptr1 - (SIGNED_VALUE)ptr2; - if (!diff) return INT2FIX(0); - return diff > 0 ? INT2NUM(1) : INT2NUM(-1); -} - -/* - * call-seq: - * ptr + n => new cptr - * - * Returns a new pointer instance that has been advanced +n+ bytes. - */ -static VALUE -rb_fiddle_ptr_plus(VALUE self, VALUE other) -{ - void *ptr; - long num, size; - - ptr = rb_fiddle_ptr2cptr(self); - size = RPTR_DATA(self)->size; - num = NUM2LONG(other); - return rb_fiddle_ptr_new((char *)ptr + num, size - num, 0); -} - -/* - * call-seq: - * ptr - n => new cptr - * - * Returns a new pointer instance that has been moved back +n+ bytes. - */ -static VALUE -rb_fiddle_ptr_minus(VALUE self, VALUE other) -{ - void *ptr; - long num, size; - - ptr = rb_fiddle_ptr2cptr(self); - size = RPTR_DATA(self)->size; - num = NUM2LONG(other); - return rb_fiddle_ptr_new((char *)ptr - num, size + num, 0); -} - -/* - * call-seq: - * ptr[index] -> an_integer - * ptr[start, length] -> a_string - * - * Returns integer stored at _index_. - * - * If _start_ and _length_ are given, a string containing the bytes from - * _start_ of _length_ will be returned. - */ -static VALUE -rb_fiddle_ptr_aref(int argc, VALUE argv[], VALUE self) -{ - VALUE arg0, arg1; - VALUE retval = Qnil; - size_t offset, len; - struct ptr_data *data; - - TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); - if (!data->ptr) rb_raise(rb_eFiddleDLError, "NULL pointer dereference"); - switch( rb_scan_args(argc, argv, "11", &arg0, &arg1) ){ - case 1: - offset = NUM2ULONG(arg0); - retval = INT2NUM(*((char *)data->ptr + offset)); - break; - case 2: - offset = NUM2ULONG(arg0); - len = NUM2ULONG(arg1); - retval = rb_str_new((char *)data->ptr + offset, len); - break; - default: - rb_bug("rb_fiddle_ptr_aref()"); - } - return retval; -} - -/* - * call-seq: - * ptr[index] = int -> int - * ptr[start, length] = string or cptr or addr -> string or dl_cptr or addr - * - * Set the value at +index+ to +int+. - * - * Or, set the memory at +start+ until +length+ with the contents of +string+, - * the memory from +dl_cptr+, or the memory pointed at by the memory address - * +addr+. - */ -static VALUE -rb_fiddle_ptr_aset(int argc, VALUE argv[], VALUE self) -{ - VALUE arg0, arg1, arg2; - VALUE retval = Qnil; - size_t offset, len; - void *mem; - struct ptr_data *data; - - TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); - if (!data->ptr) rb_raise(rb_eFiddleDLError, "NULL pointer dereference"); - switch( rb_scan_args(argc, argv, "21", &arg0, &arg1, &arg2) ){ - case 2: - offset = NUM2ULONG(arg0); - ((char*)data->ptr)[offset] = NUM2UINT(arg1); - retval = arg1; - break; - case 3: - offset = NUM2ULONG(arg0); - len = NUM2ULONG(arg1); - if (RB_TYPE_P(arg2, T_STRING)) { - mem = StringValuePtr(arg2); - } - else if( rb_obj_is_kind_of(arg2, rb_cPointer) ){ - mem = rb_fiddle_ptr2cptr(arg2); - } - else{ - mem = NUM2PTR(arg2); - } - memcpy((char *)data->ptr + offset, mem, len); - retval = arg2; - break; - default: - rb_bug("rb_fiddle_ptr_aset()"); - } - return retval; -} - -/* - * call-seq: size=(size) - * - * Set the size of this pointer to +size+ - */ -static VALUE -rb_fiddle_ptr_size_set(VALUE self, VALUE size) -{ - RPTR_DATA(self)->size = NUM2LONG(size); - return size; -} - -/* - * call-seq: size - * - * Get the size of this pointer. - */ -static VALUE -rb_fiddle_ptr_size_get(VALUE self) -{ - return LONG2NUM(RPTR_DATA(self)->size); -} - -/* - * call-seq: - * Fiddle::Pointer[val] => cptr - * to_ptr(val) => cptr - * - * Get the underlying pointer for ruby object +val+ and return it as a - * Fiddle::Pointer object. - */ -static VALUE -rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE val) -{ - VALUE ptr, wrap = val, vptr; - - if (RTEST(rb_obj_is_kind_of(val, rb_cIO))){ - rb_io_t *fptr; - FILE *fp; - GetOpenFile(val, fptr); - fp = rb_io_stdio_file(fptr); - ptr = rb_fiddle_ptr_new(fp, 0, NULL); - } - else if (RTEST(rb_obj_is_kind_of(val, rb_cString))){ - char *str = StringValuePtr(val); - wrap = val; - ptr = rb_fiddle_ptr_new(str, RSTRING_LEN(val), NULL); - } - else if ((vptr = rb_check_funcall(val, id_to_ptr, 0, 0)) != Qundef){ - if (rb_obj_is_kind_of(vptr, rb_cPointer)){ - ptr = vptr; - wrap = 0; - } - else{ - rb_raise(rb_eFiddleDLError, "to_ptr should return a Fiddle::Pointer object"); - } - } - else{ - VALUE num = rb_Integer(val); - if (num == val) wrap = 0; - ptr = rb_fiddle_ptr_new(NUM2PTR(num), 0, NULL); - } - if (wrap) RB_OBJ_WRITE(ptr, &RPTR_DATA(ptr)->wrap[0], wrap); - return ptr; -} - -/* - * call-seq: - * Fiddle::Pointer.read(address, len) => string - * - * Or read the memory at address +address+ with length +len+ and return a - * string with that memory - */ - -static VALUE -rb_fiddle_ptr_read_mem(VALUE klass, VALUE address, VALUE len) -{ - return rb_str_new((char *)NUM2PTR(address), NUM2ULONG(len)); -} - -/* - * call-seq: - * Fiddle::Pointer.write(address, str) - * - * Write bytes in +str+ to the location pointed to by +address+. - */ -static VALUE -rb_fiddle_ptr_write_mem(VALUE klass, VALUE addr, VALUE str) -{ - memcpy(NUM2PTR(addr), StringValuePtr(str), RSTRING_LEN(str)); - return str; -} - -void -Init_fiddle_pointer(void) -{ -#undef rb_intern - id_to_ptr = rb_intern("to_ptr"); - - /* Document-class: Fiddle::Pointer - * - * Fiddle::Pointer is a class to handle C pointers - * - */ - rb_cPointer = rb_define_class_under(mFiddle, "Pointer", rb_cObject); - rb_define_alloc_func(rb_cPointer, rb_fiddle_ptr_s_allocate); - rb_define_singleton_method(rb_cPointer, "malloc", rb_fiddle_ptr_s_malloc, -1); - rb_define_singleton_method(rb_cPointer, "to_ptr", rb_fiddle_ptr_s_to_ptr, 1); - rb_define_singleton_method(rb_cPointer, "[]", rb_fiddle_ptr_s_to_ptr, 1); - rb_define_singleton_method(rb_cPointer, "read", rb_fiddle_ptr_read_mem, 2); - rb_define_singleton_method(rb_cPointer, "write", rb_fiddle_ptr_write_mem, 2); - rb_define_method(rb_cPointer, "initialize", rb_fiddle_ptr_initialize, -1); - rb_define_method(rb_cPointer, "free=", rb_fiddle_ptr_free_set, 1); - rb_define_method(rb_cPointer, "free", rb_fiddle_ptr_free_get, 0); - rb_define_method(rb_cPointer, "call_free", rb_fiddle_ptr_call_free, 0); - rb_define_method(rb_cPointer, "freed?", rb_fiddle_ptr_freed_p, 0); - rb_define_method(rb_cPointer, "to_i", rb_fiddle_ptr_to_i, 0); - rb_define_method(rb_cPointer, "to_int", rb_fiddle_ptr_to_i, 0); - rb_define_method(rb_cPointer, "to_value", rb_fiddle_ptr_to_value, 0); - rb_define_method(rb_cPointer, "ptr", rb_fiddle_ptr_ptr, 0); - rb_define_method(rb_cPointer, "+@", rb_fiddle_ptr_ptr, 0); - rb_define_method(rb_cPointer, "ref", rb_fiddle_ptr_ref, 0); - rb_define_method(rb_cPointer, "-@", rb_fiddle_ptr_ref, 0); - rb_define_method(rb_cPointer, "null?", rb_fiddle_ptr_null_p, 0); - rb_define_method(rb_cPointer, "to_s", rb_fiddle_ptr_to_s, -1); - rb_define_method(rb_cPointer, "to_str", rb_fiddle_ptr_to_str, -1); - rb_define_method(rb_cPointer, "inspect", rb_fiddle_ptr_inspect, 0); - rb_define_method(rb_cPointer, "<=>", rb_fiddle_ptr_cmp, 1); - rb_define_method(rb_cPointer, "==", rb_fiddle_ptr_eql, 1); - rb_define_method(rb_cPointer, "eql?", rb_fiddle_ptr_eql, 1); - rb_define_method(rb_cPointer, "+", rb_fiddle_ptr_plus, 1); - rb_define_method(rb_cPointer, "-", rb_fiddle_ptr_minus, 1); - rb_define_method(rb_cPointer, "[]", rb_fiddle_ptr_aref, -1); - rb_define_method(rb_cPointer, "[]=", rb_fiddle_ptr_aset, -1); - rb_define_method(rb_cPointer, "size", rb_fiddle_ptr_size_get, 0); - rb_define_method(rb_cPointer, "size=", rb_fiddle_ptr_size_set, 1); - -#ifdef HAVE_RUBY_MEMORY_VIEW_H - rb_memory_view_register(rb_cPointer, &fiddle_ptr_memory_view_entry); -#endif - - /* Document-const: NULL - * - * A NULL pointer - */ - rb_define_const(mFiddle, "NULL", rb_fiddle_ptr_new(0, 0, 0)); -} diff --git a/ext/fiddle/win32/fficonfig.h b/ext/fiddle/win32/fficonfig.h deleted file mode 100644 index 776808159c..0000000000 --- a/ext/fiddle/win32/fficonfig.h +++ /dev/null @@ -1,29 +0,0 @@ -#define HAVE_ALLOCA 1 -#define HAVE_MEMCPY 1 -#define HAVE_MEMORY_H 1 -#define HAVE_STDLIB_H 1 -#define HAVE_STRING_H 1 -#define HAVE_SYS_STAT_H 1 -#define HAVE_SYS_TYPES_H 1 -#if _MSC_VER >= 1600 -#define HAVE_INTTYPES_H 1 -#define HAVE_STDINT_H 1 -#endif - -#define SIZEOF_DOUBLE 8 -#if defined(X86_WIN64) -#define SIZEOF_SIZE_T 8 -#else -#define SIZEOF_SIZE_T 4 -#endif - -#define STACK_DIRECTION -1 - -#define STDC_HEADERS 1 - -#ifdef LIBFFI_ASM -#define FFI_HIDDEN(name) -#else -#define FFI_HIDDEN -#endif - diff --git a/ext/fiddle/win32/libffi-3.2.1-mswin.patch b/ext/fiddle/win32/libffi-3.2.1-mswin.patch deleted file mode 100644 index f9100e703d..0000000000 --- a/ext/fiddle/win32/libffi-3.2.1-mswin.patch +++ /dev/null @@ -1,191 +0,0 @@ -diff -ru libffi-3.2.1/src/x86/ffi.c libffi-3.2.1/src/x86/ffi.c ---- libffi-3.2.1/src/x86/ffi.c 2014-11-08 21:47:24.000000000 +0900 -+++ libffi-3.2.1/src/x86/ffi.c 2014-12-25 18:46:14.806761900 +0900 -@@ -99,11 +99,13 @@ - i != 0; - i--, p_arg += dir, p_argv += dir) - { -+ size_t z; -+ - /* Align if necessary */ - if ((sizeof(void*) - 1) & (size_t) argp) - argp = (char *) ALIGN(argp, sizeof(void*)); - -- size_t z = (*p_arg)->size; -+ z = (*p_arg)->size; - - #ifdef X86_WIN64 - if (z > FFI_SIZEOF_ARG -@@ -202,6 +204,7 @@ - on top of stack, so that those can be moved to registers by call-handler. */ - if (stack_args_count > 0) - { -+ int i; - if (dir < 0 && stack_args_count > 1) - { - /* Reverse order if iterating arguments backwards */ -@@ -210,7 +213,6 @@ - *(ffi_arg*) p_stack_data[stack_args_count - 1] = tmp; - } - -- int i; - for (i = 0; i < stack_args_count; i++) - { - if (p_stack_data[i] != argp2) -@@ -569,11 +571,12 @@ - i < cif->nargs && passed_regs < max_stack_count; - i++, p_arg++) - { -+ size_t sz; - if ((*p_arg)->type == FFI_TYPE_FLOAT - || (*p_arg)->type == FFI_TYPE_STRUCT) - continue; - -- size_t sz = (*p_arg)->size; -+ sz = (*p_arg)->size; - if(sz == 0 || sz > FFI_SIZEOF_ARG) - continue; - -@@ -599,11 +602,13 @@ - i != 0; - i--, p_arg += dir, p_argv += dir) - { -+ size_t z; -+ - /* Align if necessary */ - if ((sizeof(void*) - 1) & (size_t) argp) - argp = (char *) ALIGN(argp, sizeof(void*)); - -- size_t z = (*p_arg)->size; -+ z = (*p_arg)->size; - - #ifdef X86_WIN64 - if (z > FFI_SIZEOF_ARG -@@ -642,7 +647,7 @@ - #endif - } - -- return (size_t)argp - (size_t)stack; -+ return (int)((size_t)argp - (size_t)stack); - } - - #define FFI_INIT_TRAMPOLINE_WIN64(TRAMP,FUN,CTX,MASK) \ -@@ -855,11 +860,12 @@ - - for (i = 0; i < cif->nargs && passed_regs <= max_regs; i++) - { -+ size_t sz; - if (cif->arg_types[i]->type == FFI_TYPE_FLOAT - || cif->arg_types[i]->type == FFI_TYPE_STRUCT) - continue; - -- size_t sz = cif->arg_types[i]->size; -+ sz = cif->arg_types[i]->size; - if (sz == 0 || sz > FFI_SIZEOF_ARG) - continue; - -diff -ru libffi-3.2.1/src/x86/ffitarget.h libffi-3.2.1/src/x86/ffitarget.h ---- libffi-3.2.1/src/x86/ffitarget.h 2014-11-08 21:47:24.000000000 +0900 -+++ libffi-3.2.1/src/x86/ffitarget.h 2014-12-22 15:45:54.000000000 +0900 -@@ -50,7 +50,9 @@ - #endif - - #define FFI_TARGET_SPECIFIC_STACK_SPACE_ALLOCATION -+#ifndef _MSC_VER - #define FFI_TARGET_HAS_COMPLEX_TYPE -+#endif - - /* ---- Generic type definitions ----------------------------------------- */ - -diff -ru libffi-3.2.1/src/x86/win64.S libffi-3.2.1/src/x86/win64.S ---- libffi-3.2.1/src/x86/win64.S 2014-11-08 21:47:24.000000000 +0900 -+++ libffi-3.2.1/src/x86/win64.S 2014-12-22 16:14:40.000000000 +0900 -@@ -127,7 +127,7 @@ - - mov rcx, QWORD PTR RVALUE[rbp] - mov DWORD PTR [rcx], eax -- jmp ret_void$ -+ jmp SHORT ret_void$ - - ret_struct2b$: - cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_SMALL_STRUCT_2B -@@ -135,7 +135,7 @@ - - mov rcx, QWORD PTR RVALUE[rbp] - mov WORD PTR [rcx], ax -- jmp ret_void$ -+ jmp SHORT ret_void$ - - ret_struct1b$: - cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_SMALL_STRUCT_1B -@@ -143,7 +143,7 @@ - - mov rcx, QWORD PTR RVALUE[rbp] - mov BYTE PTR [rcx], al -- jmp ret_void$ -+ jmp SHORT ret_void$ - - ret_uint8$: - cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_UINT8 -@@ -152,7 +152,7 @@ - mov rcx, QWORD PTR RVALUE[rbp] - movzx rax, al - mov QWORD PTR [rcx], rax -- jmp ret_void$ -+ jmp SHORT ret_void$ - - ret_sint8$: - cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_SINT8 -@@ -161,7 +161,7 @@ - mov rcx, QWORD PTR RVALUE[rbp] - movsx rax, al - mov QWORD PTR [rcx], rax -- jmp ret_void$ -+ jmp SHORT ret_void$ - - ret_uint16$: - cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_UINT16 -@@ -188,7 +188,13 @@ - mov rcx, QWORD PTR RVALUE[rbp] - mov eax, eax - mov QWORD PTR [rcx], rax -- jmp SHORT ret_void$ -+ -+ret_void$: -+ xor rax, rax -+ -+ lea rsp, QWORD PTR [rbp+16] -+ pop rbp -+ ret 0 - - ret_sint32$: - cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_SINT32 -@@ -247,13 +253,6 @@ - cdqe - mov QWORD PTR [rcx], rax - jmp SHORT ret_void$ -- --ret_void$: -- xor rax, rax -- -- lea rsp, QWORD PTR [rbp+16] -- pop rbp -- ret 0 - ffi_call_win64 ENDP - _TEXT ENDS - END -diff -ru libffi-3.2.1/include/ffi.h.in libffi-3.2.1/include/ffi.h.in ---- libffi-3.2.1/include/ffi.h.in 2014-11-08 21:47:24.000000000 +0900 -+++ libffi-3.2.1/include/ffi.h.in 2015-01-11 12:35:30.000000000 +0900 -@@ -103,6 +103,11 @@ - # undef FFI_64_BIT_MAX - # define FFI_64_BIT_MAX 9223372036854775807LL - # endif -+# ifdef _MSC_VER -+# define FFI_LONG_LONG_MAX _I64_MAX -+# undef FFI_64_BIT_MAX -+# define FFI_64_BIT_MAX 9223372036854775807I64 -+# endif - # endif - #endif - diff --git a/ext/fiddle/win32/libffi-config.rb b/ext/fiddle/win32/libffi-config.rb deleted file mode 100755 index 8e8069a943..0000000000 --- a/ext/fiddle/win32/libffi-config.rb +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/ruby -# frozen_string_literal: true -require 'fileutils' - -basedir = File.dirname(__FILE__) -conf = {} -enable = {} -until ARGV.empty? - arg = ARGV.shift - case arg - when '-C' - # ignore - when /\A--srcdir=(.*)/ - conf['SRCDIR'] = srcdir = $1 - when /\A(CC|CFLAGS|CXX|CXXFLAGS|LD|LDFLAGS)=(.*)/ - conf[$1] = $2 - when /\A--host=(.*)/ - host = $1 - when /\A--enable-([^=]+)(?:=(.*))?/ - enable[$1] = $2 || true - when /\A--disable-([^=]+)/ - enable[$1] = false - end -end - -File.foreach("#{srcdir}/configure.ac") do |line| - if /^AC_INIT\((.*)\)/ =~ line - version = $1.split(/,\s*/)[1] - version.gsub!(/\A\[|\]\z/, '') - conf['VERSION'] = version - break - end -end - -builddir = srcdir == "." ? (enable['builddir'] || ".") : "." -conf['TARGET'] = /^x64/ =~ host ? "X86_WIN64" : "X86_WIN32" - -FileUtils.mkdir_p([builddir, "#{builddir}/include", "#{builddir}/src/x86"]) -FileUtils.cp("#{basedir}/fficonfig.h", ".", preserve: true) - -hdr = File.binread("#{srcdir}/include/ffi.h.in") -hdr.gsub!(/@(\w+)@/) {conf[$1] || $&} -hdr.gsub!(/^(#if\s+)@\w+@/, '\10') -File.binwrite("#{builddir}/include/ffi.h", hdr) - -mk = File.binread("#{basedir}/libffi.mk.tmpl") -mk.gsub!(/@(\w+)@/) {conf[$1] || $&} -File.binwrite("Makefile", mk) diff --git a/ext/fiddle/win32/libffi.mk.tmpl b/ext/fiddle/win32/libffi.mk.tmpl deleted file mode 100644 index 2a16e8efec..0000000000 --- a/ext/fiddle/win32/libffi.mk.tmpl +++ /dev/null @@ -1,96 +0,0 @@ -# -*- makefile -*- -# ==================================================================== -# -# libffi Windows Makefile -# -# -# ==================================================================== -# -NAME = ffi -TARGET = @TARGET@ -CC = cl -!if "$(TARGET)" == "X86_WIN64" -AS = ml64 -!else -AS = ml -!endif -AR = link -DLEXT = dll -OBJEXT = obj -LIBEXT = lib -TOPDIR = @SRCDIR@ -CPP = $(CC) -EP -CFLAGS = @CFLAGS@ -ARFLAGS = -lib -ASFLAGS = -coff -W3 -Cx -INCLUDES= -I. -I./include -I./src/x86 \ - -I$(TOPDIR)/include -I$(TOPDIR)/include/src/x86 - -SRCDIR = $(TOPDIR)/src -WORKDIR = ./.libs -BUILDDIR= ./src -LIBNAME = lib$(NAME) -STATICLIB= $(WORKDIR)/$(LIBNAME)_convenience.$(LIBEXT) - -HEADERS = \ - ./fficonfig.h -FFI_HEADERS = \ - ./include/ffi.h \ - ./include/ffitarget.h - -!if "$(TARGET)" == "X86_WIN32" -OSSRC = win32 -!else if "$(TARGET)" == "X86_WIN64" -OSSRC = win64 -!else -! error unknown target: $(TARGET) -!endif - -OBJECTS = \ - $(BUILDDIR)/closures.$(OBJEXT) \ - $(BUILDDIR)/debug.$(OBJEXT) \ - $(BUILDDIR)/java_raw_api.$(OBJEXT) \ - $(BUILDDIR)/prep_cif.$(OBJEXT) \ - $(BUILDDIR)/raw_api.$(OBJEXT) \ - $(BUILDDIR)/types.$(OBJEXT) \ - $(BUILDDIR)/x86/ffi.$(OBJEXT) \ - $(BUILDDIR)/x86/$(OSSRC).$(OBJEXT) -ASMSRCS = \ - $(BUILDDIR)/x86/$(OSSRC).asm - -.SUFFIXES : .S .asm - -all: $(WORKDIR) $(STATICLIB) - -{$(SRCDIR)}.c{$(BUILDDIR)}.$(OBJEXT): - $(CC) -c $(CFLAGS) $(INCLUDES) -Fo$(@:\=/) -Fd$(WORKDIR)/$(NAME)-src $(<:\=/) - -{$(SRCDIR)/x86}.c{$(BUILDDIR)/x86}.$(OBJEXT): - $(CC) -c $(CFLAGS) $(INCLUDES) -Fo$(@:\=/) -Fd$(WORKDIR)/$(NAME)-src $(<:\=/) - -{$(SRCDIR)/x86}.S{$(BUILDDIR)/x86}.asm: - $(CPP) $(CFLAGS) $(INCLUDES) $(<:\=/) >$(@:\=/) - -{$(BUILDDIR)/x86}.asm{$(BUILDDIR)/x86}.$(OBJEXT): - cd $(@D) && $(AS) -c $(ASFLAGS) -Fo $(@F) $(<F) - -$(BUILDDIR)/x86/$(OSSRC).asm: $(SRCDIR)/x86/$(OSSRC).S - -$(OBJECTS): $(FFI_HEADERS) $(HEADERS) - -$(WORKDIR): - -@if not exist "$(WORKDIR:/=\)\$(NULL)" mkdir $(WORKDIR:/=\) - -$(STATICLIB): $(WORKDIR) $(OBJECTS) - $(AR) $(ARFLAGS) -out:$(STATICLIB) @<< - $(OBJECTS) -<< - -clean: - -@del /Q $(OBJECTS:/=\) 2>NUL - -@del /Q $(ASMSRCS:/=\) 2>NUL - -@del /Q /S $(WORKDIR:/=\) 2>NUL - -distclean: clean - -@del /Q $(HEADERS:/=\) $(FFI_HEADERS:/=\) 2>NUL - -@del /Q Makefile 2>NUL diff --git a/ext/io/console/console.c b/ext/io/console/console.c index 7130c29a8b..7ddaf071a8 100644 --- a/ext/io/console/console.c +++ b/ext/io/console/console.c @@ -4,7 +4,7 @@ */ static const char *const -IO_CONSOLE_VERSION = "0.7.2"; +IO_CONSOLE_VERSION = "0.8.2"; #include "ruby.h" #include "ruby/io.h" @@ -81,9 +81,14 @@ getattr(int fd, conmode *t) #define CSI "\x1b\x5b" -static ID id_getc, id_console, id_close; +static ID id_getc, id_close; static ID id_gets, id_flush, id_chomp_bang; +#ifndef HAVE_RB_INTERNED_STR_CSTR +# define rb_str_to_interned_str(str) rb_str_freeze(str) +# define rb_interned_str_cstr(str) rb_str_freeze(rb_usascii_str_new_cstr(str)) +#endif + #if defined HAVE_RUBY_FIBER_SCHEDULER_H # include "ruby/fiber/scheduler.h" #elif defined HAVE_RB_SCHEDULER_TIMEOUT @@ -125,7 +130,14 @@ io_get_write_io_fallback(VALUE io) #define rb_io_get_write_io io_get_write_io_fallback #endif -#define sys_fail(io) rb_sys_fail_str(rb_io_path(io)) +#ifndef DHAVE_RB_SYSERR_FAIL_STR +# define rb_syserr_fail_str(e, mesg) rb_exc_raise(rb_syserr_new_str(e, mesg)) +#endif + +#define sys_fail(io) do { \ + int err = errno; \ + rb_syserr_fail_str(err, rb_io_path(io)); \ +} while (0) #ifndef HAVE_RB_F_SEND #ifndef RB_PASS_CALLED_KEYWORDS @@ -828,6 +840,9 @@ console_winsize(VALUE io) return rb_assoc_new(INT2NUM(winsize_row(&ws)), INT2NUM(winsize_col(&ws))); } +static VALUE console_scroll(VALUE io, int line); +static VALUE console_goto(VALUE io, VALUE y, VALUE x); + /* * call-seq: * io.winsize = [rows, columns] @@ -844,6 +859,8 @@ console_set_winsize(VALUE io, VALUE size) #if defined _WIN32 HANDLE wh; int newrow, newcol; + COORD oldsize; + SMALL_RECT oldwindow; BOOL ret; #endif VALUE row, col, xpixel, ypixel; @@ -879,18 +896,62 @@ console_set_winsize(VALUE io, VALUE size) if (!GetConsoleScreenBufferInfo(wh, &ws)) { rb_syserr_fail(LAST_ERROR, "GetConsoleScreenBufferInfo"); } + oldsize = ws.dwSize; + oldwindow = ws.srWindow; + if (ws.srWindow.Right + 1 < newcol) { + ws.dwSize.X = newcol; + } + if (ws.dwSize.Y < newrow) { + ws.dwSize.Y = newrow; + } + /* expand screen buffer first if needed */ + if (!SetConsoleScreenBufferSize(wh, ws.dwSize)) { + rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo"); + } + /* refresh ws for new dwMaximumWindowSize */ + if (!GetConsoleScreenBufferInfo(wh, &ws)) { + rb_syserr_fail(LAST_ERROR, "GetConsoleScreenBufferInfo"); + } + /* check new size before modifying buffer content */ + if (newrow <= 0 || newcol <= 0 || + newrow > ws.dwMaximumWindowSize.Y || + newcol > ws.dwMaximumWindowSize.X) { + SetConsoleScreenBufferSize(wh, oldsize); + /* remove scrollbar if possible */ + SetConsoleWindowInfo(wh, TRUE, &oldwindow); + rb_raise(rb_eArgError, "out of range winsize: [%d, %d]", newrow, newcol); + } + /* shrink screen buffer width */ ws.dwSize.X = newcol; - ret = SetConsoleScreenBufferSize(wh, ws.dwSize); + /* shrink screen buffer height if window height were the same */ + if (oldsize.Y == ws.srWindow.Bottom - ws.srWindow.Top + 1) { + ws.dwSize.Y = newrow; + } ws.srWindow.Left = 0; - ws.srWindow.Top = 0; - ws.srWindow.Right = newcol-1; - ws.srWindow.Bottom = newrow-1; + ws.srWindow.Right = newcol - 1; + ws.srWindow.Bottom = ws.srWindow.Top + newrow -1; + if (ws.dwCursorPosition.Y > ws.srWindow.Bottom) { + console_scroll(io, ws.dwCursorPosition.Y - ws.srWindow.Bottom); + ws.dwCursorPosition.Y = ws.srWindow.Bottom; + console_goto(io, INT2FIX(ws.dwCursorPosition.Y), INT2FIX(ws.dwCursorPosition.X)); + } + if (ws.srWindow.Bottom > ws.dwSize.Y - 1) { + console_scroll(io, ws.srWindow.Bottom - (ws.dwSize.Y - 1)); + ws.dwCursorPosition.Y -= ws.srWindow.Bottom - (ws.dwSize.Y - 1); + console_goto(io, INT2FIX(ws.dwCursorPosition.Y), INT2FIX(ws.dwCursorPosition.X)); + ws.srWindow.Bottom = ws.dwSize.Y - 1; + } + ws.srWindow.Top = ws.srWindow.Bottom - (newrow - 1); + /* perform changes to winsize */ if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) { - rb_syserr_fail(LAST_ERROR, "SetConsoleWindowInfo"); + int last_error = LAST_ERROR; + SetConsoleScreenBufferSize(wh, oldsize); + rb_syserr_fail(last_error, "SetConsoleWindowInfo"); } - /* retry when shrinking buffer after shrunk window */ - if (!ret && !SetConsoleScreenBufferSize(wh, ws.dwSize)) { - rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo"); + /* perform screen buffer shrinking if necessary */ + if ((ws.dwSize.Y < oldsize.Y || ws.dwSize.X < oldsize.X) && + !SetConsoleScreenBufferSize(wh, ws.dwSize)) { + rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo"); } /* remove scrollbar if possible */ if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) { @@ -1071,6 +1132,9 @@ console_scroll(VALUE io, int line) return io; } +#define GPERF_DOWNCASE 1 +#define GPERF_CASE_STRCMP 1 +#define gperf_case_strcmp STRCASECMP #include "win32_vk.inc" /* @@ -1206,7 +1270,7 @@ console_cursor_pos(VALUE io) if (!GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), &ws)) { rb_syserr_fail(LAST_ERROR, 0); } - return rb_assoc_new(UINT2NUM(ws.dwCursorPosition.Y), UINT2NUM(ws.dwCursorPosition.X)); + return rb_assoc_new(UINT2NUM(ws.dwCursorPosition.Y - ws.srWindow.Top), UINT2NUM(ws.dwCursorPosition.X)); #else static const struct query_args query = {"\033[6n", 0}; VALUE resp = console_vt_response(0, 0, io, &query); @@ -1239,11 +1303,17 @@ static VALUE console_goto(VALUE io, VALUE y, VALUE x) { #ifdef _WIN32 - COORD pos; - int fd = GetWriteFD(io); - pos.X = NUM2UINT(x); - pos.Y = NUM2UINT(y); - if (!SetConsoleCursorPosition((HANDLE)rb_w32_get_osfhandle(fd), pos)) { + HANDLE h; + rb_console_size_t ws; + COORD *pos = &ws.dwCursorPosition; + + h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io)); + if (!GetConsoleScreenBufferInfo(h, &ws)) { + rb_syserr_fail(LAST_ERROR, 0); + } + pos->X = NUM2UINT(x); + pos->Y = ws.srWindow.Top + NUM2UINT(y); + if (!SetConsoleCursorPosition(h, *pos)) { rb_syserr_fail(LAST_ERROR, 0); } #else @@ -1535,10 +1605,8 @@ console_clear_screen(VALUE io) static VALUE io_open_descriptor_fallback(VALUE klass, int descriptor, int mode, VALUE path, VALUE timeout, void *encoding) { - rb_update_max_fd(descriptor); - VALUE arguments[2] = { - INT2NUM(descriptor), + (rb_update_max_fd(descriptor), INT2NUM(descriptor)), INT2FIX(mode), }; @@ -1563,6 +1631,62 @@ rb_io_closed_p(VALUE io) } #endif +#if defined(RB_EXT_RACTOR_SAFE) && defined(HAVE_RB_RACTOR_LOCAL_STORAGE_VALUE_NEWKEY) +# define USE_RACTOR_STORAGE 1 +#else +# define USE_RACTOR_STORAGE 0 +#endif + +#if USE_RACTOR_STORAGE +#include "ruby/ractor.h" +static rb_ractor_local_key_t key_console_dev; + +static bool +console_dev_get(VALUE klass, VALUE *dev) +{ + return rb_ractor_local_storage_value_lookup(key_console_dev, dev); +} + +static void +console_dev_set(VALUE klass, VALUE value) +{ + rb_ractor_local_storage_value_set(key_console_dev, value); +} + +static void +console_dev_remove(VALUE klass) +{ + console_dev_set(klass, Qnil); +} + +#else + +static ID id_console; + +static int +console_dev_get(VALUE klass, VALUE *dev) +{ + if (rb_const_defined(klass, id_console)) { + *dev = rb_const_get(klass, id_console); + return 1; + } + return 0; +} + +static void +console_dev_set(VALUE klass, VALUE value) +{ + rb_const_set(klass, id_console, value); +} + +static void +console_dev_remove(VALUE klass) +{ + rb_const_remove(klass, id_console); +} + +#endif + /* * call-seq: * IO.console -> #<File:/dev/tty> @@ -1582,8 +1706,6 @@ console_dev(int argc, VALUE *argv, VALUE klass) VALUE con = 0; VALUE sym = 0; - rb_check_arity(argc, 0, UNLIMITED_ARGUMENTS); - if (argc) { Check_Type(sym = argv[0], T_SYMBOL); } @@ -1591,10 +1713,9 @@ console_dev(int argc, VALUE *argv, VALUE klass) // Force the class to be File. if (klass == rb_cIO) klass = rb_cFile; - if (rb_const_defined(klass, id_console)) { - con = rb_const_get(klass, id_console); + if (console_dev_get(klass, &con)) { if (!RB_TYPE_P(con, T_FILE) || RTEST(rb_io_closed_p(con))) { - rb_const_remove(klass, id_console); + console_dev_remove(klass); con = 0; } } @@ -1603,7 +1724,7 @@ console_dev(int argc, VALUE *argv, VALUE klass) if (sym == ID2SYM(id_close) && argc == 1) { if (con) { rb_io_close(con); - rb_const_remove(klass, id_console); + console_dev_remove(klass); con = 0; } return Qnil; @@ -1623,7 +1744,6 @@ console_dev(int argc, VALUE *argv, VALUE klass) #endif #ifdef CONSOLE_DEVICE_FOR_WRITING VALUE out; - rb_io_t *ofptr; #endif int fd; VALUE path = rb_obj_freeze(rb_str_new2(CONSOLE_DEVICE)); @@ -1645,7 +1765,7 @@ console_dev(int argc, VALUE *argv, VALUE klass) #ifdef CONSOLE_DEVICE_FOR_WRITING rb_io_set_write_io(con, out); #endif - rb_const_set(klass, id_console, con); + console_dev_set(klass, con); } if (sym) { @@ -1756,18 +1876,81 @@ io_getpass(int argc, VALUE *argv, VALUE io) return str_chomp(str); } +#if defined(_WIN32) || defined(HAVE_TTYNAME_R) || defined(HAVE_TTYNAME) +/* + * call-seq: + * io.ttyname -> string or nil + * + * Returns name of associated terminal (tty) if +io+ is not a tty. + * Returns +nil+ otherwise. + */ +static VALUE +console_ttyname(VALUE io) +{ + int fd = rb_io_descriptor(io); + if (!isatty(fd)) return Qnil; +# if defined _WIN32 + return rb_usascii_str_new_lit("con"); +# elif defined HAVE_TTYNAME_R + { + char termname[1024], *tn = termname; + size_t size = sizeof(termname); + int e; + if (ttyname_r(fd, tn, size) == 0) + return rb_interned_str_cstr(tn); + if ((e = errno) == ERANGE) { + VALUE s = rb_str_new(0, size); + while (1) { + tn = RSTRING_PTR(s); + size = rb_str_capacity(s); + if (ttyname_r(fd, tn, size) == 0) { + return rb_str_to_interned_str(rb_str_resize(s, strlen(tn))); + } + if ((e = errno) != ERANGE) break; + if ((size *= 2) >= INT_MAX/2) break; + rb_str_resize(s, size); + } + } + rb_syserr_fail_str(e, rb_sprintf("ttyname_r(%d)", fd)); + UNREACHABLE_RETURN(Qnil); + } +# elif defined HAVE_TTYNAME + { + const char *tn = ttyname(fd); + if (!tn) { + int e = errno; + rb_syserr_fail_str(e, rb_sprintf("ttyname(%d)", fd)); + } + return rb_interned_str_cstr(tn); + } +# else +# error No ttyname function +# endif +} +#else +# define console_ttyname rb_f_notimplement +#endif + /* * IO console methods */ void Init_console(void) { +#if USE_RACTOR_STORAGE + RB_EXT_RACTOR_SAFE(true); +#endif + #undef rb_intern +#if USE_RACTOR_STORAGE + key_console_dev = rb_ractor_local_storage_value_newkey(); +#else + id_console = rb_intern("console"); +#endif id_getc = rb_intern("getc"); id_gets = rb_intern("gets"); id_flush = rb_intern("flush"); id_chomp_bang = rb_intern("chomp!"); - id_console = rb_intern("console"); id_close = rb_intern("close"); #define init_rawmode_opt_id(name) \ rawmode_opt_ids[kwd_##name] = rb_intern(#name) @@ -1815,6 +1998,7 @@ InitVM_console(void) rb_define_method(rb_cIO, "pressed?", console_key_pressed_p, 1); rb_define_method(rb_cIO, "check_winsize_changed", console_check_winsize_changed, 0); rb_define_method(rb_cIO, "getpass", console_getpass, -1); + rb_define_method(rb_cIO, "ttyname", console_ttyname, 0); rb_define_singleton_method(rb_cIO, "console", console_dev, -1); { /* :stopdoc: */ @@ -1826,7 +2010,7 @@ InitVM_console(void) { /* :stopdoc: */ cConmode = rb_define_class_under(rb_cIO, "ConsoleMode", rb_cObject); - rb_define_const(cConmode, "VERSION", rb_str_new_cstr(IO_CONSOLE_VERSION)); + rb_define_const(cConmode, "VERSION", rb_obj_freeze(rb_str_new_cstr(IO_CONSOLE_VERSION))); rb_define_alloc_func(cConmode, conmode_alloc); rb_undef_method(cConmode, "initialize"); rb_define_method(cConmode, "initialize_copy", conmode_init_copy, 1); diff --git a/ext/io/console/depend b/ext/io/console/depend index 59ca3442c2..150a138d4d 100644 --- a/ext/io/console/depend +++ b/ext/io/console/depend @@ -139,6 +139,7 @@ console.o: $(hdrdir)/ruby/internal/intern/re.h console.o: $(hdrdir)/ruby/internal/intern/ruby.h console.o: $(hdrdir)/ruby/internal/intern/select.h console.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +console.o: $(hdrdir)/ruby/internal/intern/set.h console.o: $(hdrdir)/ruby/internal/intern/signal.h console.o: $(hdrdir)/ruby/internal/intern/sprintf.h console.o: $(hdrdir)/ruby/internal/intern/string.h @@ -158,6 +159,7 @@ console.o: $(hdrdir)/ruby/internal/special_consts.h console.o: $(hdrdir)/ruby/internal/static_assert.h console.o: $(hdrdir)/ruby/internal/stdalign.h console.o: $(hdrdir)/ruby/internal/stdbool.h +console.o: $(hdrdir)/ruby/internal/stdckdint.h console.o: $(hdrdir)/ruby/internal/symbol.h console.o: $(hdrdir)/ruby/internal/value.h console.o: $(hdrdir)/ruby/internal/value_type.h @@ -168,6 +170,7 @@ console.o: $(hdrdir)/ruby/io.h console.o: $(hdrdir)/ruby/missing.h console.o: $(hdrdir)/ruby/onigmo.h console.o: $(hdrdir)/ruby/oniguruma.h +console.o: $(hdrdir)/ruby/ractor.h console.o: $(hdrdir)/ruby/ruby.h console.o: $(hdrdir)/ruby/st.h console.o: $(hdrdir)/ruby/subst.h @@ -184,7 +187,7 @@ win32_vk.inc: win32_vk.list -e 'n=$$F[1] and (n.strip!; /\AVK_/=~n) and' \ -e 'puts(%[#ifndef #{n}\n# define #{n} UNDEFINED_VK\n#endif])' \ $< && \ - gperf --ignore-case -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k* $< \ + gperf --ignore-case -L ANSI-C -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k* $< \ | sed -f $(top_srcdir)/tool/gperf.sed \ ) > $(@F) diff --git a/ext/io/console/extconf.rb b/ext/io/console/extconf.rb index a72403c7f9..dd3d221ae5 100644 --- a/ext/io/console/extconf.rb +++ b/ext/io/console/extconf.rb @@ -1,13 +1,30 @@ # frozen_string_literal: false require 'mkmf' -have_func("rb_io_path") -have_func("rb_io_descriptor") -have_func("rb_io_get_write_io") -have_func("rb_io_closed_p") -have_func("rb_io_open_descriptor") +# `--target-rbconfig` compatibility for Ruby 3.3 or earlier +# See https://bugs.ruby-lang.org/issues/20345 +MakeMakefile::RbConfig ||= ::RbConfig -ok = true if RUBY_ENGINE == "ruby" || RUBY_ENGINE == "truffleruby" +have_func("rb_syserr_fail_str(0, Qnil)") or +have_func("rb_syserr_new_str(0, Qnil)") or + abort + +have_func("rb_interned_str_cstr") +have_func("rb_io_path", "ruby/io.h") +have_func("rb_io_descriptor", "ruby/io.h") +have_func("rb_io_get_write_io", "ruby/io.h") +have_func("rb_io_closed_p", "ruby/io.h") +have_func("rb_io_open_descriptor", "ruby/io.h") +have_func("rb_ractor_local_storage_value_newkey") + +is_wasi = /wasi/ =~ MakeMakefile::RbConfig::CONFIG["platform"] +# `ok` can be `true`, `false`, or `nil`: +# * `true` : Required headers and functions available, proceed regular build. +# * `false`: Required headers or functions not available, abort build. +# * `nil` : Unsupported compilation target, generate dummy Makefile. +# +# Skip building io/console on WASI, as it does not support termios.h. +ok = true if (RUBY_ENGINE == "ruby" && !is_wasi) || RUBY_ENGINE == "truffleruby" hdr = nil case when macro_defined?("_WIN32", "") @@ -35,6 +52,7 @@ when true elsif have_func("rb_scheduler_timeout") # 3.0 have_func("rb_io_wait") end + have_func("ttyname_r") or have_func("ttyname") create_makefile("io/console") {|conf| conf << "\n""VK_HEADER = #{vk_header}\n" } diff --git a/ext/io/console/io-console.gemspec b/ext/io/console/io-console.gemspec index f9c1729cb7..0a19992734 100644 --- a/ext/io/console/io-console.gemspec +++ b/ext/io/console/io-console.gemspec @@ -23,7 +23,8 @@ Gem::Specification.new do |s| s.require_path = %[lib] s.files = %w[ .document - LICENSE.txt + BSDL + COPYING README.md ext/io/console/console.c ext/io/console/extconf.rb diff --git a/ext/io/console/win32_vk.inc b/ext/io/console/win32_vk.inc index cbec7bef15..348e6be5ed 100644 --- a/ext/io/console/win32_vk.inc +++ b/ext/io/console/win32_vk.inc @@ -480,7 +480,7 @@ # define VK_OEM_CLEAR UNDEFINED_VK #endif /* ANSI-C code produced by gperf version 3.1 */ -/* Command-line: gperf --ignore-case -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k'*' win32_vk.list */ +/* Command-line: gperf --ignore-case -L ANSI-C -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k'*' win32_vk.list */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ @@ -509,11 +509,10 @@ #error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>." #endif -#define gperf_offsetof(s, n) (short)offsetof(struct s##_t, s##_str##n) #line 1 "win32_vk.list" struct vktable {short ofs; unsigned short vk;}; -static const struct vktable *console_win32_vk(/*const char *, unsigned int*/); +static const struct vktable *console_win32_vk(const char *, size_t); #line 5 "win32_vk.list" struct vktable; /* maximum key range = 245, duplicates = 0 */ @@ -1007,368 +1006,368 @@ console_win32_vk (register const char *str, register size_t len) {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, #line 40 "win32_vk.list" - {gperf_offsetof(stringpool, 12), VK_UP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str12, VK_UP}, #line 52 "win32_vk.list" - {gperf_offsetof(stringpool, 13), VK_APPS}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str13, VK_APPS}, #line 159 "win32_vk.list" - {gperf_offsetof(stringpool, 14), VK_CRSEL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str14, VK_CRSEL}, #line 34 "win32_vk.list" - {gperf_offsetof(stringpool, 15), VK_SPACE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str15, VK_SPACE}, #line 95 "win32_vk.list" - {gperf_offsetof(stringpool, 16), VK_SCROLL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str16, VK_SCROLL}, #line 29 "win32_vk.list" - {gperf_offsetof(stringpool, 17), VK_ESCAPE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str17, VK_ESCAPE}, #line 9 "win32_vk.list" - {gperf_offsetof(stringpool, 18), VK_CANCEL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str18, VK_CANCEL}, #line 32 "win32_vk.list" - {gperf_offsetof(stringpool, 19), VK_ACCEPT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str19, VK_ACCEPT}, #line 66 "win32_vk.list" - {gperf_offsetof(stringpool, 20), VK_SEPARATOR}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str20, VK_SEPARATOR}, #line 43 "win32_vk.list" - {gperf_offsetof(stringpool, 21), VK_SELECT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str21, VK_SELECT}, #line 18 "win32_vk.list" - {gperf_offsetof(stringpool, 22), VK_CONTROL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str22, VK_CONTROL}, #line 166 "win32_vk.list" - {gperf_offsetof(stringpool, 23), VK_OEM_CLEAR}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str23, VK_OEM_CLEAR}, #line 145 "win32_vk.list" - {gperf_offsetof(stringpool, 24), VK_OEM_RESET}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str24, VK_OEM_RESET}, #line 155 "win32_vk.list" - {gperf_offsetof(stringpool, 25), VK_OEM_AUTO}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str25, VK_OEM_AUTO}, #line 151 "win32_vk.list" - {gperf_offsetof(stringpool, 26), VK_OEM_CUSEL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str26, VK_OEM_CUSEL}, {-1}, #line 22 "win32_vk.list" - {gperf_offsetof(stringpool, 28), VK_KANA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str28, VK_KANA}, #line 127 "win32_vk.list" - {gperf_offsetof(stringpool, 29), VK_OEM_PLUS}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str29, VK_OEM_PLUS}, #line 35 "win32_vk.list" - {gperf_offsetof(stringpool, 30), VK_PRIOR}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str30, VK_PRIOR}, #line 152 "win32_vk.list" - {gperf_offsetof(stringpool, 31), VK_OEM_ATTN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str31, VK_OEM_ATTN}, #line 20 "win32_vk.list" - {gperf_offsetof(stringpool, 32), VK_PAUSE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str32, VK_PAUSE}, #line 13 "win32_vk.list" - {gperf_offsetof(stringpool, 33), VK_BACK}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str33, VK_BACK}, #line 144 "win32_vk.list" - {gperf_offsetof(stringpool, 34), VK_PACKET}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str34, VK_PACKET}, #line 105 "win32_vk.list" - {gperf_offsetof(stringpool, 35), VK_RCONTROL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str35, VK_RCONTROL}, #line 104 "win32_vk.list" - {gperf_offsetof(stringpool, 36), VK_LCONTROL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str36, VK_LCONTROL}, #line 37 "win32_vk.list" - {gperf_offsetof(stringpool, 37), VK_END}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str37, VK_END}, #line 38 "win32_vk.list" - {gperf_offsetof(stringpool, 38), VK_HOME}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str38, VK_HOME}, #line 44 "win32_vk.list" - {gperf_offsetof(stringpool, 39), VK_PRINT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str39, VK_PRINT}, #line 94 "win32_vk.list" - {gperf_offsetof(stringpool, 40), VK_NUMLOCK}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str40, VK_NUMLOCK}, #line 39 "win32_vk.list" - {gperf_offsetof(stringpool, 41), VK_LEFT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str41, VK_LEFT}, #line 25 "win32_vk.list" - {gperf_offsetof(stringpool, 42), VK_JUNJA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str42, VK_JUNJA}, #line 19 "win32_vk.list" - {gperf_offsetof(stringpool, 43), VK_MENU}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str43, VK_MENU}, #line 150 "win32_vk.list" - {gperf_offsetof(stringpool, 44), VK_OEM_WSCTRL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str44, VK_OEM_WSCTRL}, #line 156 "win32_vk.list" - {gperf_offsetof(stringpool, 45), VK_OEM_ENLW}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str45, VK_OEM_ENLW}, #line 36 "win32_vk.list" - {gperf_offsetof(stringpool, 46), VK_NEXT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str46, VK_NEXT}, #line 51 "win32_vk.list" - {gperf_offsetof(stringpool, 47), VK_RWIN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str47, VK_RWIN}, #line 50 "win32_vk.list" - {gperf_offsetof(stringpool, 48), VK_LWIN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str48, VK_LWIN}, #line 21 "win32_vk.list" - {gperf_offsetof(stringpool, 49), VK_CAPITAL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str49, VK_CAPITAL}, #line 49 "win32_vk.list" - {gperf_offsetof(stringpool, 50), VK_HELP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str50, VK_HELP}, #line 164 "win32_vk.list" - {gperf_offsetof(stringpool, 51), VK_NONAME}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str51, VK_NONAME}, #line 8 "win32_vk.list" - {gperf_offsetof(stringpool, 52), VK_RBUTTON}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str52, VK_RBUTTON}, #line 7 "win32_vk.list" - {gperf_offsetof(stringpool, 53), VK_LBUTTON}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str53, VK_LBUTTON}, #line 96 "win32_vk.list" - {gperf_offsetof(stringpool, 54), VK_OEM_NEC_EQUAL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str54, VK_OEM_NEC_EQUAL}, {-1}, #line 47 "win32_vk.list" - {gperf_offsetof(stringpool, 56), VK_INSERT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str56, VK_INSERT}, #line 27 "win32_vk.list" - {gperf_offsetof(stringpool, 57), VK_HANJA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str57, VK_HANJA}, {-1}, {-1}, #line 46 "win32_vk.list" - {gperf_offsetof(stringpool, 60), VK_SNAPSHOT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str60, VK_SNAPSHOT}, #line 158 "win32_vk.list" - {gperf_offsetof(stringpool, 61), VK_ATTN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str61, VK_ATTN}, #line 14 "win32_vk.list" - {gperf_offsetof(stringpool, 62), VK_TAB}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str62, VK_TAB}, #line 157 "win32_vk.list" - {gperf_offsetof(stringpool, 63), VK_OEM_BACKTAB}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str63, VK_OEM_BACKTAB}, #line 143 "win32_vk.list" - {gperf_offsetof(stringpool, 64), VK_ICO_CLEAR}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str64, VK_ICO_CLEAR}, #line 30 "win32_vk.list" - {gperf_offsetof(stringpool, 65), VK_CONVERT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str65, VK_CONVERT}, #line 16 "win32_vk.list" - {gperf_offsetof(stringpool, 66), VK_RETURN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str66, VK_RETURN}, #line 146 "win32_vk.list" - {gperf_offsetof(stringpool, 67), VK_OEM_JUMP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str67, VK_OEM_JUMP}, {-1}, {-1}, {-1}, #line 111 "win32_vk.list" - {gperf_offsetof(stringpool, 71), VK_BROWSER_STOP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str71, VK_BROWSER_STOP}, #line 26 "win32_vk.list" - {gperf_offsetof(stringpool, 72), VK_FINAL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str72, VK_FINAL}, #line 163 "win32_vk.list" - {gperf_offsetof(stringpool, 73), VK_ZOOM}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str73, VK_ZOOM}, #line 28 "win32_vk.list" - {gperf_offsetof(stringpool, 74), VK_KANJI}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str74, VK_KANJI}, #line 48 "win32_vk.list" - {gperf_offsetof(stringpool, 75), VK_DELETE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str75, VK_DELETE}, #line 128 "win32_vk.list" - {gperf_offsetof(stringpool, 76), VK_OEM_COMMA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str76, VK_OEM_COMMA}, #line 67 "win32_vk.list" - {gperf_offsetof(stringpool, 77), VK_SUBTRACT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str77, VK_SUBTRACT}, {-1}, #line 10 "win32_vk.list" - {gperf_offsetof(stringpool, 79), VK_MBUTTON}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str79, VK_MBUTTON}, #line 78 "win32_vk.list" - {gperf_offsetof(stringpool, 80), VK_F9}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str80, VK_F9}, #line 17 "win32_vk.list" - {gperf_offsetof(stringpool, 81), VK_SHIFT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str81, VK_SHIFT}, #line 103 "win32_vk.list" - {gperf_offsetof(stringpool, 82), VK_RSHIFT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str82, VK_RSHIFT}, #line 102 "win32_vk.list" - {gperf_offsetof(stringpool, 83), VK_LSHIFT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str83, VK_LSHIFT}, #line 65 "win32_vk.list" - {gperf_offsetof(stringpool, 84), VK_ADD}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str84, VK_ADD}, #line 31 "win32_vk.list" - {gperf_offsetof(stringpool, 85), VK_NONCONVERT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str85, VK_NONCONVERT}, #line 160 "win32_vk.list" - {gperf_offsetof(stringpool, 86), VK_EXSEL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str86, VK_EXSEL}, #line 126 "win32_vk.list" - {gperf_offsetof(stringpool, 87), VK_OEM_1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str87, VK_OEM_1}, #line 138 "win32_vk.list" - {gperf_offsetof(stringpool, 88), VK_OEM_AX}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str88, VK_OEM_AX}, #line 108 "win32_vk.list" - {gperf_offsetof(stringpool, 89), VK_BROWSER_BACK}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str89, VK_BROWSER_BACK}, #line 137 "win32_vk.list" - {gperf_offsetof(stringpool, 90), VK_OEM_8}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str90, VK_OEM_8}, #line 129 "win32_vk.list" - {gperf_offsetof(stringpool, 91), VK_OEM_MINUS}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str91, VK_OEM_MINUS}, #line 162 "win32_vk.list" - {gperf_offsetof(stringpool, 92), VK_PLAY}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str92, VK_PLAY}, #line 131 "win32_vk.list" - {gperf_offsetof(stringpool, 93), VK_OEM_2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str93, VK_OEM_2}, #line 15 "win32_vk.list" - {gperf_offsetof(stringpool, 94), VK_CLEAR}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str94, VK_CLEAR}, #line 99 "win32_vk.list" - {gperf_offsetof(stringpool, 95), VK_OEM_FJ_TOUROKU}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str95, VK_OEM_FJ_TOUROKU}, #line 147 "win32_vk.list" - {gperf_offsetof(stringpool, 96), VK_OEM_PA1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str96, VK_OEM_PA1}, #line 140 "win32_vk.list" - {gperf_offsetof(stringpool, 97), VK_ICO_HELP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str97, VK_ICO_HELP}, #line 112 "win32_vk.list" - {gperf_offsetof(stringpool, 98), VK_BROWSER_SEARCH}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str98, VK_BROWSER_SEARCH}, #line 53 "win32_vk.list" - {gperf_offsetof(stringpool, 99), VK_SLEEP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str99, VK_SLEEP}, {-1}, #line 70 "win32_vk.list" - {gperf_offsetof(stringpool, 101), VK_F1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str101, VK_F1}, #line 148 "win32_vk.list" - {gperf_offsetof(stringpool, 102), VK_OEM_PA2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str102, VK_OEM_PA2}, #line 154 "win32_vk.list" - {gperf_offsetof(stringpool, 103), VK_OEM_COPY}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str103, VK_OEM_COPY}, #line 77 "win32_vk.list" - {gperf_offsetof(stringpool, 104), VK_F8}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str104, VK_F8}, #line 88 "win32_vk.list" - {gperf_offsetof(stringpool, 105), VK_F19}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str105, VK_F19}, #line 41 "win32_vk.list" - {gperf_offsetof(stringpool, 106), VK_RIGHT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str106, VK_RIGHT}, #line 71 "win32_vk.list" - {gperf_offsetof(stringpool, 107), VK_F2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str107, VK_F2}, #line 135 "win32_vk.list" - {gperf_offsetof(stringpool, 108), VK_OEM_6}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str108, VK_OEM_6}, #line 87 "win32_vk.list" - {gperf_offsetof(stringpool, 109), VK_F18}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str109, VK_F18}, {-1}, #line 117 "win32_vk.list" - {gperf_offsetof(stringpool, 111), VK_VOLUME_UP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str111, VK_VOLUME_UP}, {-1}, {-1}, #line 120 "win32_vk.list" - {gperf_offsetof(stringpool, 114), VK_MEDIA_STOP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str114, VK_MEDIA_STOP}, #line 130 "win32_vk.list" - {gperf_offsetof(stringpool, 115), VK_OEM_PERIOD}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str115, VK_OEM_PERIOD}, {-1}, #line 161 "win32_vk.list" - {gperf_offsetof(stringpool, 117), VK_EREOF}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str117, VK_EREOF}, {-1}, {-1}, {-1}, #line 114 "win32_vk.list" - {gperf_offsetof(stringpool, 121), VK_BROWSER_HOME}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str121, VK_BROWSER_HOME}, #line 75 "win32_vk.list" - {gperf_offsetof(stringpool, 122), VK_F6}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str122, VK_F6}, {-1}, #line 110 "win32_vk.list" - {gperf_offsetof(stringpool, 124), VK_BROWSER_REFRESH}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str124, VK_BROWSER_REFRESH}, {-1}, #line 165 "win32_vk.list" - {gperf_offsetof(stringpool, 126), VK_PA1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str126, VK_PA1}, #line 142 "win32_vk.list" - {gperf_offsetof(stringpool, 127), VK_PROCESSKEY}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str127, VK_PROCESSKEY}, #line 68 "win32_vk.list" - {gperf_offsetof(stringpool, 128), VK_DECIMAL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str128, VK_DECIMAL}, #line 132 "win32_vk.list" - {gperf_offsetof(stringpool, 129), VK_OEM_3}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str129, VK_OEM_3}, #line 107 "win32_vk.list" - {gperf_offsetof(stringpool, 130), VK_RMENU}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str130, VK_RMENU}, #line 106 "win32_vk.list" - {gperf_offsetof(stringpool, 131), VK_LMENU}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str131, VK_LMENU}, #line 98 "win32_vk.list" - {gperf_offsetof(stringpool, 132), VK_OEM_FJ_MASSHOU}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str132, VK_OEM_FJ_MASSHOU}, #line 54 "win32_vk.list" - {gperf_offsetof(stringpool, 133), VK_NUMPAD0}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str133, VK_NUMPAD0}, #line 24 "win32_vk.list" - {gperf_offsetof(stringpool, 134), VK_HANGUL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str134, VK_HANGUL}, #line 63 "win32_vk.list" - {gperf_offsetof(stringpool, 135), VK_NUMPAD9}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str135, VK_NUMPAD9}, #line 23 "win32_vk.list" - {gperf_offsetof(stringpool, 136), VK_HANGEUL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str136, VK_HANGEUL}, #line 134 "win32_vk.list" - {gperf_offsetof(stringpool, 137), VK_OEM_5}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str137, VK_OEM_5}, #line 149 "win32_vk.list" - {gperf_offsetof(stringpool, 138), VK_OEM_PA3}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str138, VK_OEM_PA3}, #line 115 "win32_vk.list" - {gperf_offsetof(stringpool, 139), VK_VOLUME_MUTE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str139, VK_VOLUME_MUTE}, #line 133 "win32_vk.list" - {gperf_offsetof(stringpool, 140), VK_OEM_4}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str140, VK_OEM_4}, #line 122 "win32_vk.list" - {gperf_offsetof(stringpool, 141), VK_LAUNCH_MAIL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str141, VK_LAUNCH_MAIL}, #line 97 "win32_vk.list" - {gperf_offsetof(stringpool, 142), VK_OEM_FJ_JISHO}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str142, VK_OEM_FJ_JISHO}, #line 72 "win32_vk.list" - {gperf_offsetof(stringpool, 143), VK_F3}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str143, VK_F3}, #line 101 "win32_vk.list" - {gperf_offsetof(stringpool, 144), VK_OEM_FJ_ROYA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str144, VK_OEM_FJ_ROYA}, #line 100 "win32_vk.list" - {gperf_offsetof(stringpool, 145), VK_OEM_FJ_LOYA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str145, VK_OEM_FJ_LOYA}, {-1}, #line 42 "win32_vk.list" - {gperf_offsetof(stringpool, 147), VK_DOWN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str147, VK_DOWN}, {-1}, #line 153 "win32_vk.list" - {gperf_offsetof(stringpool, 149), VK_OEM_FINISH}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str149, VK_OEM_FINISH}, {-1}, #line 74 "win32_vk.list" - {gperf_offsetof(stringpool, 151), VK_F5}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str151, VK_F5}, {-1}, #line 136 "win32_vk.list" - {gperf_offsetof(stringpool, 153), VK_OEM_7}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str153, VK_OEM_7}, #line 73 "win32_vk.list" - {gperf_offsetof(stringpool, 154), VK_F4}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str154, VK_F4}, #line 86 "win32_vk.list" - {gperf_offsetof(stringpool, 155), VK_F17}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str155, VK_F17}, #line 55 "win32_vk.list" - {gperf_offsetof(stringpool, 156), VK_NUMPAD1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str156, VK_NUMPAD1}, #line 141 "win32_vk.list" - {gperf_offsetof(stringpool, 157), VK_ICO_00}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str157, VK_ICO_00}, {-1}, #line 62 "win32_vk.list" - {gperf_offsetof(stringpool, 159), VK_NUMPAD8}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str159, VK_NUMPAD8}, {-1}, {-1}, #line 56 "win32_vk.list" - {gperf_offsetof(stringpool, 162), VK_NUMPAD2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str162, VK_NUMPAD2}, {-1}, #line 124 "win32_vk.list" - {gperf_offsetof(stringpool, 164), VK_LAUNCH_APP1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str164, VK_LAUNCH_APP1}, #line 109 "win32_vk.list" - {gperf_offsetof(stringpool, 165), VK_BROWSER_FORWARD}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str165, VK_BROWSER_FORWARD}, {-1}, #line 76 "win32_vk.list" - {gperf_offsetof(stringpool, 167), VK_F7}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str167, VK_F7}, {-1}, {-1}, #line 125 "win32_vk.list" - {gperf_offsetof(stringpool, 170), VK_LAUNCH_APP2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str170, VK_LAUNCH_APP2}, #line 64 "win32_vk.list" - {gperf_offsetof(stringpool, 171), VK_MULTIPLY}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str171, VK_MULTIPLY}, {-1}, {-1}, #line 45 "win32_vk.list" - {gperf_offsetof(stringpool, 174), VK_EXECUTE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str174, VK_EXECUTE}, {-1}, #line 113 "win32_vk.list" - {gperf_offsetof(stringpool, 176), VK_BROWSER_FAVORITES}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str176, VK_BROWSER_FAVORITES}, #line 60 "win32_vk.list" - {gperf_offsetof(stringpool, 177), VK_NUMPAD6}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str177, VK_NUMPAD6}, {-1}, #line 85 "win32_vk.list" - {gperf_offsetof(stringpool, 179), VK_F16}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str179, VK_F16}, {-1}, {-1}, #line 79 "win32_vk.list" - {gperf_offsetof(stringpool, 182), VK_F10}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str182, VK_F10}, {-1}, {-1}, #line 116 "win32_vk.list" - {gperf_offsetof(stringpool, 185), VK_VOLUME_DOWN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str185, VK_VOLUME_DOWN}, {-1}, {-1}, #line 89 "win32_vk.list" - {gperf_offsetof(stringpool, 188), VK_F20}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str188, VK_F20}, #line 119 "win32_vk.list" - {gperf_offsetof(stringpool, 189), VK_MEDIA_PREV_TRACK}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str189, VK_MEDIA_PREV_TRACK}, {-1}, #line 33 "win32_vk.list" - {gperf_offsetof(stringpool, 191), VK_MODECHANGE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str191, VK_MODECHANGE}, {-1}, {-1}, {-1}, {-1}, {-1}, #line 83 "win32_vk.list" - {gperf_offsetof(stringpool, 197), VK_F14}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str197, VK_F14}, #line 57 "win32_vk.list" - {gperf_offsetof(stringpool, 198), VK_NUMPAD3}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str198, VK_NUMPAD3}, #line 11 "win32_vk.list" - {gperf_offsetof(stringpool, 199), VK_XBUTTON1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str199, VK_XBUTTON1}, {-1}, {-1}, {-1}, #line 93 "win32_vk.list" - {gperf_offsetof(stringpool, 203), VK_F24}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str203, VK_F24}, {-1}, #line 12 "win32_vk.list" - {gperf_offsetof(stringpool, 205), VK_XBUTTON2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str205, VK_XBUTTON2}, #line 59 "win32_vk.list" - {gperf_offsetof(stringpool, 206), VK_NUMPAD5}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str206, VK_NUMPAD5}, {-1}, {-1}, #line 58 "win32_vk.list" - {gperf_offsetof(stringpool, 209), VK_NUMPAD4}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str209, VK_NUMPAD4}, {-1}, {-1}, {-1}, {-1}, {-1}, #line 121 "win32_vk.list" - {gperf_offsetof(stringpool, 215), VK_MEDIA_PLAY_PAUSE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str215, VK_MEDIA_PLAY_PAUSE}, {-1}, #line 123 "win32_vk.list" - {gperf_offsetof(stringpool, 217), VK_LAUNCH_MEDIA_SELECT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str217, VK_LAUNCH_MEDIA_SELECT}, #line 80 "win32_vk.list" - {gperf_offsetof(stringpool, 218), VK_F11}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str218, VK_F11}, {-1}, #line 139 "win32_vk.list" - {gperf_offsetof(stringpool, 220), VK_OEM_102}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str220, VK_OEM_102}, #line 118 "win32_vk.list" - {gperf_offsetof(stringpool, 221), VK_MEDIA_NEXT_TRACK}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str221, VK_MEDIA_NEXT_TRACK}, #line 61 "win32_vk.list" - {gperf_offsetof(stringpool, 222), VK_NUMPAD7}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str222, VK_NUMPAD7}, {-1}, #line 90 "win32_vk.list" - {gperf_offsetof(stringpool, 224), VK_F21}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str224, VK_F21}, {-1}, #line 82 "win32_vk.list" - {gperf_offsetof(stringpool, 226), VK_F13}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str226, VK_F13}, {-1}, {-1}, #line 81 "win32_vk.list" - {gperf_offsetof(stringpool, 229), VK_F12}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str229, VK_F12}, {-1}, {-1}, #line 92 "win32_vk.list" - {gperf_offsetof(stringpool, 232), VK_F23}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str232, VK_F23}, {-1}, {-1}, #line 91 "win32_vk.list" - {gperf_offsetof(stringpool, 235), VK_F22}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str235, VK_F22}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, #line 84 "win32_vk.list" - {gperf_offsetof(stringpool, 242), VK_F15}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str242, VK_F15}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, #line 69 "win32_vk.list" - {gperf_offsetof(stringpool, 256), VK_DIVIDE} + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str256, VK_DIVIDE} }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) diff --git a/ext/io/console/win32_vk.list b/ext/io/console/win32_vk.list index 7909a4d1f0..5df3d6da57 100644 --- a/ext/io/console/win32_vk.list +++ b/ext/io/console/win32_vk.list @@ -1,6 +1,6 @@ %{ struct vktable {short ofs; unsigned short vk;}; -static const struct vktable *console_win32_vk(/*!ANSI{*/const char *, unsigned int/*}!ANSI*/); +static const struct vktable *console_win32_vk(const char *, size_t); %} struct vktable %% diff --git a/ext/io/nonblock/depend b/ext/io/nonblock/depend index 48384fca62..e78a05c316 100644 --- a/ext/io/nonblock/depend +++ b/ext/io/nonblock/depend @@ -138,6 +138,7 @@ nonblock.o: $(hdrdir)/ruby/internal/intern/re.h nonblock.o: $(hdrdir)/ruby/internal/intern/ruby.h nonblock.o: $(hdrdir)/ruby/internal/intern/select.h nonblock.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +nonblock.o: $(hdrdir)/ruby/internal/intern/set.h nonblock.o: $(hdrdir)/ruby/internal/intern/signal.h nonblock.o: $(hdrdir)/ruby/internal/intern/sprintf.h nonblock.o: $(hdrdir)/ruby/internal/intern/string.h @@ -157,6 +158,7 @@ nonblock.o: $(hdrdir)/ruby/internal/special_consts.h nonblock.o: $(hdrdir)/ruby/internal/static_assert.h nonblock.o: $(hdrdir)/ruby/internal/stdalign.h nonblock.o: $(hdrdir)/ruby/internal/stdbool.h +nonblock.o: $(hdrdir)/ruby/internal/stdckdint.h nonblock.o: $(hdrdir)/ruby/internal/symbol.h nonblock.o: $(hdrdir)/ruby/internal/value.h nonblock.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/io/nonblock/extconf.rb b/ext/io/nonblock/extconf.rb index a1e6075c9b..505c9e6252 100644 --- a/ext/io/nonblock/extconf.rb +++ b/ext/io/nonblock/extconf.rb @@ -7,7 +7,7 @@ unless RUBY_ENGINE == 'ruby' return end -have_func("rb_io_descriptor") +have_func("rb_io_descriptor", "ruby/io.h") hdr = %w"fcntl.h" if have_macro("O_NONBLOCK", hdr) and diff --git a/ext/io/nonblock/io-nonblock.gemspec b/ext/io/nonblock/io-nonblock.gemspec index 6a16c8b03b..4f5b8cef6f 100644 --- a/ext/io/nonblock/io-nonblock.gemspec +++ b/ext/io/nonblock/io-nonblock.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |spec| spec.name = "io-nonblock" - spec.version = "0.3.0" + spec.version = "0.3.2" spec.authors = ["Nobu Nakada"] spec.email = ["nobu@ruby-lang.org"] @@ -8,7 +8,7 @@ Gem::Specification.new do |spec| spec.description = %q{Enables non-blocking mode with IO class} spec.homepage = "https://github.com/ruby/io-nonblock" spec.licenses = ["Ruby", "BSD-2-Clause"] - spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0") + spec.required_ruby_version = Gem::Requirement.new(">= 3.0") spec.metadata["homepage_uri"] = spec.homepage spec.metadata["source_code_uri"] = spec.homepage diff --git a/ext/io/nonblock/nonblock.c b/ext/io/nonblock/nonblock.c index d90538f735..cd40ea3335 100644 --- a/ext/io/nonblock/nonblock.c +++ b/ext/io/nonblock/nonblock.c @@ -197,6 +197,10 @@ rb_io_nonblock_block(int argc, VALUE *argv, VALUE self) void Init_nonblock(void) { +#ifdef HAVE_RB_EXT_RACTOR_SAFE + rb_ext_ractor_safe(true); +#endif + #ifndef RUBY_IO_NONBLOCK_METHODS rb_define_method(rb_cIO, "nonblock?", rb_io_nonblock_p, 0); rb_define_method(rb_cIO, "nonblock=", rb_io_nonblock_set, 1); diff --git a/ext/io/wait/depend b/ext/io/wait/depend index 83cf8f94c8..41d53c1400 100644 --- a/ext/io/wait/depend +++ b/ext/io/wait/depend @@ -1,5 +1,4 @@ # AUTOGENERATED DEPENDENCIES START -# wait.o: $(hdrdir)/ruby/assert.h # not in 2.6 wait.o: $(RUBY_EXTCONF_H) wait.o: $(arch_hdrdir)/ruby/config.h wait.o: $(hdrdir)/ruby.h @@ -139,6 +138,7 @@ wait.o: $(hdrdir)/ruby/internal/intern/re.h wait.o: $(hdrdir)/ruby/internal/intern/ruby.h wait.o: $(hdrdir)/ruby/internal/intern/select.h wait.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +wait.o: $(hdrdir)/ruby/internal/intern/set.h wait.o: $(hdrdir)/ruby/internal/intern/signal.h wait.o: $(hdrdir)/ruby/internal/intern/sprintf.h wait.o: $(hdrdir)/ruby/internal/intern/string.h @@ -158,6 +158,7 @@ wait.o: $(hdrdir)/ruby/internal/special_consts.h wait.o: $(hdrdir)/ruby/internal/static_assert.h wait.o: $(hdrdir)/ruby/internal/stdalign.h wait.o: $(hdrdir)/ruby/internal/stdbool.h +wait.o: $(hdrdir)/ruby/internal/stdckdint.h wait.o: $(hdrdir)/ruby/internal/symbol.h wait.o: $(hdrdir)/ruby/internal/value.h wait.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/io/wait/extconf.rb b/ext/io/wait/extconf.rb index e63c046187..00c455a45c 100644 --- a/ext/io/wait/extconf.rb +++ b/ext/io/wait/extconf.rb @@ -1,25 +1,4 @@ # frozen_string_literal: false require 'mkmf' -if RUBY_VERSION < "2.6" - File.write("Makefile", dummy_makefile($srcdir).join("")) -else - target = "io/wait" - have_func("rb_io_wait") - have_func("rb_io_descriptor") - unless macro_defined?("DOSISH", "#include <ruby.h>") - have_header(ioctl_h = "sys/ioctl.h") or ioctl_h = nil - fionread = %w[sys/ioctl.h sys/filio.h sys/socket.h].find do |h| - have_macro("FIONREAD", [h, ioctl_h].compact) - end - if fionread - $defs << "-DFIONREAD_HEADER=\"<#{fionread}>\"" - create_makefile(target) - end - else - if have_func("rb_w32_ioctlsocket", "ruby.h") - have_func("rb_w32_is_socket", "ruby.h") - create_makefile(target) - end - end -end +create_makefile("io/wait") diff --git a/ext/io/wait/io-wait.gemspec b/ext/io/wait/io-wait.gemspec index e850e10bf9..c1c6172589 100644 --- a/ext/io/wait/io-wait.gemspec +++ b/ext/io/wait/io-wait.gemspec @@ -1,4 +1,4 @@ -_VERSION = "0.3.1" +_VERSION = "0.4.0" Gem::Specification.new do |spec| spec.name = "io-wait" @@ -10,24 +10,25 @@ Gem::Specification.new do |spec| spec.description = %q{Waits until IO is readable or writable without blocking.} spec.homepage = "https://github.com/ruby/io-wait" spec.licenses = ["Ruby", "BSD-2-Clause"] + spec.required_ruby_version = Gem::Requirement.new(">= 3.2") spec.metadata["homepage_uri"] = spec.homepage spec.metadata["source_code_uri"] = spec.homepage - spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject do |f| - File.identical?(f, __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features|rakelib)/|\.(?:git|travis|circleci)|appveyor|Rakefile)}) - end - end + jruby = true if Gem::Platform.new('java') =~ spec.platform or RUBY_ENGINE == 'jruby' + dir, gemspec = File.split(__FILE__) + excludes = [ + *%w[:^/.git* :^/Gemfile* :^/Rakefile* :^/bin/ :^/test/ :^/rakelib/ :^*.java], + *(jruby ? %w[:^/ext/io] : %w[:^/ext/java]), + ":(exclude,literal,top)#{gemspec}" + ] + files = IO.popen(%w[git ls-files -z --] + excludes, chdir: dir, &:read).split("\x0") + + spec.files = files spec.bindir = "exe" spec.executables = [] spec.require_paths = ["lib"] - jruby = true if Gem::Platform.new('java') =~ spec.platform or RUBY_ENGINE == 'jruby' - spec.files.delete_if do |f| - f.end_with?(".java") or - f.start_with?("ext/") && (jruby ^ f.start_with?("ext/java/")) - end if jruby spec.platform = 'java' spec.files << "lib/io/wait.jar" diff --git a/ext/io/wait/wait.c b/ext/io/wait/wait.c index 8835670e59..f7575191fe 100644 --- a/ext/io/wait/wait.c +++ b/ext/io/wait/wait.c @@ -11,427 +11,13 @@ **********************************************************************/ -#include "ruby.h" -#include "ruby/io.h" - -#include <sys/types.h> -#if defined(HAVE_UNISTD_H) && (defined(__sun)) -#include <unistd.h> -#endif -#if defined(HAVE_SYS_IOCTL_H) -#include <sys/ioctl.h> -#endif -#if defined(FIONREAD_HEADER) -#include FIONREAD_HEADER -#endif - -#ifdef HAVE_RB_W32_IOCTLSOCKET -#define ioctl ioctlsocket -#define ioctl_arg u_long -#define ioctl_arg2num(i) ULONG2NUM(i) -#else -#define ioctl_arg int -#define ioctl_arg2num(i) INT2NUM(i) -#endif - -#ifdef HAVE_RB_W32_IS_SOCKET -#define FIONREAD_POSSIBLE_P(fd) rb_w32_is_socket(fd) -#else -#define FIONREAD_POSSIBLE_P(fd) ((void)(fd),Qtrue) -#endif - -#ifndef HAVE_RB_IO_WAIT -static struct timeval * -get_timeout(int argc, VALUE *argv, struct timeval *timerec) -{ - VALUE timeout = Qnil; - rb_check_arity(argc, 0, 1); - if (!argc || NIL_P(timeout = argv[0])) { - return NULL; - } - else { - *timerec = rb_time_interval(timeout); - return timerec; - } -} - -static int -wait_for_single_fd(rb_io_t *fptr, int events, struct timeval *tv) -{ - int i = rb_wait_for_single_fd(fptr->fd, events, tv); - if (i < 0) - rb_sys_fail(0); - rb_io_check_closed(fptr); - return (i & events); -} -#endif - -/* - * call-seq: - * io.nread -> int - * - * Returns number of bytes that can be read without blocking. - * Returns zero if no information available. - * - * You must require 'io/wait' to use this method. - */ - -static VALUE -io_nread(VALUE io) -{ - rb_io_t *fptr; - int len; - ioctl_arg n; - - GetOpenFile(io, fptr); - rb_io_check_readable(fptr); - len = rb_io_read_pending(fptr); - if (len > 0) return INT2FIX(len); - -#ifdef HAVE_RB_IO_DESCRIPTOR - int fd = rb_io_descriptor(io); -#else - int fd = fptr->fd; -#endif - - if (!FIONREAD_POSSIBLE_P(fd)) return INT2FIX(0); - if (ioctl(fd, FIONREAD, &n)) return INT2FIX(0); - if (n > 0) return ioctl_arg2num(n); - return INT2FIX(0); -} - -#ifdef HAVE_RB_IO_WAIT -static VALUE -io_wait_event(VALUE io, int event, VALUE timeout, int return_io) -{ - VALUE result = rb_io_wait(io, RB_INT2NUM(event), timeout); - - if (!RB_TEST(result)) { - return Qnil; - } - - int mask = RB_NUM2INT(result); - - if (mask & event) { - if (return_io) - return io; - else - return result; - } - else { - return Qfalse; - } -} -#endif - -/* - * call-seq: - * io.ready? -> truthy or falsy - * - * Returns a truthy value if input available without blocking, or a - * falsy value. - * - * You must require 'io/wait' to use this method. - */ - -static VALUE -io_ready_p(VALUE io) -{ - rb_io_t *fptr; -#ifndef HAVE_RB_IO_WAIT - struct timeval tv = {0, 0}; -#endif - - GetOpenFile(io, fptr); - rb_io_check_readable(fptr); - if (rb_io_read_pending(fptr)) return Qtrue; - -#ifndef HAVE_RB_IO_WAIT - return wait_for_single_fd(fptr, RB_WAITFD_IN, &tv) ? Qtrue : Qfalse; -#else - return io_wait_event(io, RUBY_IO_READABLE, RB_INT2NUM(0), 1); -#endif -} - -/* Ruby 3.2+ can define these methods. This macro indicates that case. */ -#ifndef RUBY_IO_WAIT_METHODS - -/* - * call-seq: - * io.wait_readable -> truthy or falsy - * io.wait_readable(timeout) -> truthy or falsy - * - * Waits until IO is readable and returns a truthy value, or a falsy - * value when times out. Returns a truthy value immediately when - * buffered data is available. - * - * You must require 'io/wait' to use this method. - */ - -static VALUE -io_wait_readable(int argc, VALUE *argv, VALUE io) -{ - rb_io_t *fptr; -#ifndef HAVE_RB_IO_WAIT - struct timeval timerec; - struct timeval *tv; -#endif - - GetOpenFile(io, fptr); - rb_io_check_readable(fptr); - -#ifndef HAVE_RB_IO_WAIT - tv = get_timeout(argc, argv, &timerec); -#endif - if (rb_io_read_pending(fptr)) return Qtrue; - -#ifndef HAVE_RB_IO_WAIT - if (wait_for_single_fd(fptr, RB_WAITFD_IN, tv)) { - return io; - } - return Qnil; -#else - rb_check_arity(argc, 0, 1); - VALUE timeout = (argc == 1 ? argv[0] : Qnil); - - return io_wait_event(io, RUBY_IO_READABLE, timeout, 1); -#endif -} +#include "ruby.h" /* abi_version */ /* - * call-seq: - * io.wait_writable -> truthy or falsy - * io.wait_writable(timeout) -> truthy or falsy - * - * Waits until IO is writable and returns a truthy value or a falsy - * value when times out. - * - * You must require 'io/wait' to use this method. - */ -static VALUE -io_wait_writable(int argc, VALUE *argv, VALUE io) -{ - rb_io_t *fptr; -#ifndef HAVE_RB_IO_WAIT - struct timeval timerec; - struct timeval *tv; -#endif - - GetOpenFile(io, fptr); - rb_io_check_writable(fptr); - -#ifndef HAVE_RB_IO_WAIT - tv = get_timeout(argc, argv, &timerec); - if (wait_for_single_fd(fptr, RB_WAITFD_OUT, tv)) { - return io; - } - return Qnil; -#else - rb_check_arity(argc, 0, 1); - VALUE timeout = (argc == 1 ? argv[0] : Qnil); - - return io_wait_event(io, RUBY_IO_WRITABLE, timeout, 1); -#endif -} - -#ifdef HAVE_RB_IO_WAIT -/* - * call-seq: - * io.wait_priority -> truthy or falsy - * io.wait_priority(timeout) -> truthy or falsy - * - * Waits until IO is priority and returns a truthy value or a falsy - * value when times out. Priority data is sent and received using - * the Socket::MSG_OOB flag and is typically limited to streams. - * - * You must require 'io/wait' to use this method. - */ -static VALUE -io_wait_priority(int argc, VALUE *argv, VALUE io) -{ - rb_io_t *fptr = NULL; - - RB_IO_POINTER(io, fptr); - rb_io_check_readable(fptr); - - if (rb_io_read_pending(fptr)) return Qtrue; - - rb_check_arity(argc, 0, 1); - VALUE timeout = argc == 1 ? argv[0] : Qnil; - - return io_wait_event(io, RUBY_IO_PRIORITY, timeout, 1); -} -#endif - -static int -wait_mode_sym(VALUE mode) -{ - if (mode == ID2SYM(rb_intern("r"))) { - return RB_WAITFD_IN; - } - if (mode == ID2SYM(rb_intern("read"))) { - return RB_WAITFD_IN; - } - if (mode == ID2SYM(rb_intern("readable"))) { - return RB_WAITFD_IN; - } - if (mode == ID2SYM(rb_intern("w"))) { - return RB_WAITFD_OUT; - } - if (mode == ID2SYM(rb_intern("write"))) { - return RB_WAITFD_OUT; - } - if (mode == ID2SYM(rb_intern("writable"))) { - return RB_WAITFD_OUT; - } - if (mode == ID2SYM(rb_intern("rw"))) { - return RB_WAITFD_IN|RB_WAITFD_OUT; - } - if (mode == ID2SYM(rb_intern("read_write"))) { - return RB_WAITFD_IN|RB_WAITFD_OUT; - } - if (mode == ID2SYM(rb_intern("readable_writable"))) { - return RB_WAITFD_IN|RB_WAITFD_OUT; - } - rb_raise(rb_eArgError, "unsupported mode: %"PRIsVALUE, mode); - return 0; -} - -#ifdef HAVE_RB_IO_WAIT -static inline rb_io_event_t -io_event_from_value(VALUE value) -{ - int events = RB_NUM2INT(value); - - if (events <= 0) rb_raise(rb_eArgError, "Events must be positive integer!"); - - return events; -} -#endif - -/* - * call-seq: - * io.wait(events, timeout) -> event mask, false or nil - * io.wait(timeout = nil, mode = :read) -> self, true, or false - * - * Waits until the IO becomes ready for the specified events and returns the - * subset of events that become ready, or a falsy value when times out. - * - * The events can be a bit mask of +IO::READABLE+, +IO::WRITABLE+ or - * +IO::PRIORITY+. - * - * Returns a truthy value immediately when buffered data is available. - * - * Optional parameter +mode+ is one of +:read+, +:write+, or - * +:read_write+. - * - * You must require 'io/wait' to use this method. - */ - -static VALUE -io_wait(int argc, VALUE *argv, VALUE io) -{ -#ifndef HAVE_RB_IO_WAIT - rb_io_t *fptr; - struct timeval timerec; - struct timeval *tv = NULL; - int event = 0; - int i; - - GetOpenFile(io, fptr); - for (i = 0; i < argc; ++i) { - if (SYMBOL_P(argv[i])) { - event |= wait_mode_sym(argv[i]); - } - else { - *(tv = &timerec) = rb_time_interval(argv[i]); - } - } - /* rb_time_interval() and might_mode() might convert the argument */ - rb_io_check_closed(fptr); - if (!event) event = RB_WAITFD_IN; - if ((event & RB_WAITFD_IN) && rb_io_read_pending(fptr)) - return Qtrue; - if (wait_for_single_fd(fptr, event, tv)) - return io; - return Qnil; -#else - VALUE timeout = Qundef; - rb_io_event_t events = 0; - int i, return_io = 0; - - /* The documented signature for this method is actually incorrect. - * A single timeout is allowed in any position, and multiple symbols can be given. - * Whether this is intentional or not, I don't know, and as such I consider this to - * be a legacy/slow path. */ - if (argc != 2 || (RB_SYMBOL_P(argv[0]) || RB_SYMBOL_P(argv[1]))) { - /* We'd prefer to return the actual mask, but this form would return the io itself: */ - return_io = 1; - - /* Slow/messy path: */ - for (i = 0; i < argc; i += 1) { - if (RB_SYMBOL_P(argv[i])) { - events |= wait_mode_sym(argv[i]); - } - else if (timeout == Qundef) { - rb_time_interval(timeout = argv[i]); - } - else { - rb_raise(rb_eArgError, "timeout given more than once"); - } - } - - if (timeout == Qundef) timeout = Qnil; - - if (events == 0) { - events = RUBY_IO_READABLE; - } - } - else /* argc == 2 and neither are symbols */ { - /* This is the fast path: */ - events = io_event_from_value(argv[0]); - timeout = argv[1]; - } - - if (events & RUBY_IO_READABLE) { - rb_io_t *fptr = NULL; - RB_IO_POINTER(io, fptr); - - if (rb_io_read_pending(fptr)) { - /* This was the original behaviour: */ - if (return_io) return Qtrue; - /* New behaviour always returns an event mask: */ - else return RB_INT2NUM(RUBY_IO_READABLE); - } - } - - return io_wait_event(io, events, timeout, return_io); -#endif -} - -#endif /* RUBY_IO_WAIT_METHODS */ - -/* - * IO wait methods + * IO wait methods are built in ruby now, just for backward compatibility. */ void Init_wait(void) { -#ifdef HAVE_RB_EXT_RACTOR_SAFE - RB_EXT_RACTOR_SAFE(true); -#endif - - rb_define_method(rb_cIO, "nread", io_nread, 0); - rb_define_method(rb_cIO, "ready?", io_ready_p, 0); - -#ifndef RUBY_IO_WAIT_METHODS - rb_define_method(rb_cIO, "wait", io_wait, -1); - - rb_define_method(rb_cIO, "wait_readable", io_wait_readable, -1); - rb_define_method(rb_cIO, "wait_writable", io_wait_writable, -1); -#ifdef HAVE_RB_IO_WAIT - rb_define_method(rb_cIO, "wait_priority", io_wait_priority, -1); -#endif -#endif } diff --git a/ext/json/fbuffer/fbuffer.h b/ext/json/fbuffer/fbuffer.h index dc8f406b5b..752d153b31 100644 --- a/ext/json/fbuffer/fbuffer.h +++ b/ext/json/fbuffer/fbuffer.h @@ -1,89 +1,71 @@ - #ifndef _FBUFFER_H_ #define _FBUFFER_H_ -#include "ruby.h" - -#ifndef RHASH_SIZE -#define RHASH_SIZE(hsh) (RHASH(hsh)->tbl->num_entries) -#endif - -#ifndef RFLOAT_VALUE -#define RFLOAT_VALUE(val) (RFLOAT(val)->value) -#endif - -#ifndef RARRAY_LEN -#define RARRAY_LEN(ARRAY) RARRAY(ARRAY)->len -#endif -#ifndef RSTRING_PTR -#define RSTRING_PTR(string) RSTRING(string)->ptr -#endif -#ifndef RSTRING_LEN -#define RSTRING_LEN(string) RSTRING(string)->len -#endif +#include "../json.h" +#include "../vendor/jeaiii-ltoa.h" -#ifdef PRIsVALUE -# define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj) -# define RB_OBJ_STRING(obj) (obj) -#else -# define PRIsVALUE "s" -# define RB_OBJ_CLASSNAME(obj) rb_obj_classname(obj) -# define RB_OBJ_STRING(obj) StringValueCStr(obj) -#endif - -#ifdef HAVE_RUBY_ENCODING_H -#include "ruby/encoding.h" -#define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding()) -#else -#define FORCE_UTF8(obj) -#endif - -/* We don't need to guard objects for rbx, so let's do nothing at all. */ -#ifndef RB_GC_GUARD -#define RB_GC_GUARD(object) -#endif +enum fbuffer_type { + FBUFFER_HEAP_ALLOCATED = 0, + FBUFFER_STACK_ALLOCATED = 1, +}; typedef struct FBufferStruct { + enum fbuffer_type type; unsigned long initial_length; - char *ptr; unsigned long len; unsigned long capa; +#if JSON_DEBUG + unsigned long requested; +#endif + char *ptr; + VALUE io; } FBuffer; +#define FBUFFER_STACK_SIZE 512 +#define FBUFFER_IO_BUFFER_SIZE (16384 - 1) #define FBUFFER_INITIAL_LENGTH_DEFAULT 1024 -#define FBUFFER_PTR(fb) (fb->ptr) -#define FBUFFER_LEN(fb) (fb->len) -#define FBUFFER_CAPA(fb) (fb->capa) +#define FBUFFER_PTR(fb) ((fb)->ptr) +#define FBUFFER_LEN(fb) ((fb)->len) +#define FBUFFER_CAPA(fb) ((fb)->capa) #define FBUFFER_PAIR(fb) FBUFFER_PTR(fb), FBUFFER_LEN(fb) -static FBuffer *fbuffer_alloc(unsigned long initial_length); static void fbuffer_free(FBuffer *fb); static void fbuffer_clear(FBuffer *fb); static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len); -#ifdef JSON_GENERATOR static void fbuffer_append_long(FBuffer *fb, long number); +static inline void fbuffer_append_char(FBuffer *fb, char newchr); +static VALUE fbuffer_finalize(FBuffer *fb); + +static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *stack_buffer, long stack_buffer_size) +{ + fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT; + if (stack_buffer) { + fb->type = FBUFFER_STACK_ALLOCATED; + fb->ptr = stack_buffer; + fb->capa = stack_buffer_size; + } +#if JSON_DEBUG + fb->requested = 0; #endif -static void fbuffer_append_char(FBuffer *fb, char newchr); -#ifdef JSON_GENERATOR -static FBuffer *fbuffer_dup(FBuffer *fb); -static VALUE fbuffer_to_s(FBuffer *fb); -#endif +} -static FBuffer *fbuffer_alloc(unsigned long initial_length) +static inline void fbuffer_consumed(FBuffer *fb, unsigned long consumed) { - FBuffer *fb; - if (initial_length <= 0) initial_length = FBUFFER_INITIAL_LENGTH_DEFAULT; - fb = ALLOC(FBuffer); - memset((void *) fb, 0, sizeof(FBuffer)); - fb->initial_length = initial_length; - return fb; +#if JSON_DEBUG + if (consumed > fb->requested) { + rb_bug("fbuffer: Out of bound write"); + } + fb->requested = 0; +#endif + fb->len += consumed; } static void fbuffer_free(FBuffer *fb) { - if (fb->ptr) ruby_xfree(fb->ptr); - ruby_xfree(fb); + if (fb->ptr && fb->type == FBUFFER_HEAP_ALLOCATED) { + ruby_xfree(fb->ptr); + } } static void fbuffer_clear(FBuffer *fb) @@ -91,97 +73,175 @@ static void fbuffer_clear(FBuffer *fb) fb->len = 0; } -static void fbuffer_inc_capa(FBuffer *fb, unsigned long requested) +static void fbuffer_flush(FBuffer *fb) +{ + rb_io_write(fb->io, rb_utf8_str_new(fb->ptr, fb->len)); + fbuffer_clear(fb); +} + +static void fbuffer_realloc(FBuffer *fb, unsigned long required) { + if (required > fb->capa) { + if (fb->type == FBUFFER_STACK_ALLOCATED) { + const char *old_buffer = fb->ptr; + fb->ptr = ALLOC_N(char, required); + fb->type = FBUFFER_HEAP_ALLOCATED; + MEMCPY(fb->ptr, old_buffer, char, fb->len); + } else { + REALLOC_N(fb->ptr, char, required); + } + fb->capa = required; + } +} + +static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested) +{ + if (RB_UNLIKELY(fb->io)) { + if (fb->capa < FBUFFER_IO_BUFFER_SIZE) { + fbuffer_realloc(fb, FBUFFER_IO_BUFFER_SIZE); + } else { + fbuffer_flush(fb); + } + + if (RB_LIKELY(requested < fb->capa)) { + return; + } + } + unsigned long required; - if (!fb->ptr) { + if (RB_UNLIKELY(!fb->ptr)) { fb->ptr = ALLOC_N(char, fb->initial_length); fb->capa = fb->initial_length; } for (required = fb->capa; requested > required - fb->len; required <<= 1); - if (required > fb->capa) { - REALLOC_N(fb->ptr, char, required); - fb->capa = required; + fbuffer_realloc(fb, required); +} + +static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested) +{ +#if JSON_DEBUG + fb->requested = requested; +#endif + + if (RB_UNLIKELY(requested > fb->capa - fb->len)) { + fbuffer_do_inc_capa(fb, requested); } } -static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len) +static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr, unsigned long len) +{ + MEMCPY(fb->ptr + fb->len, newstr, char, len); + fbuffer_consumed(fb, len); +} + +static inline void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len) { if (len > 0) { fbuffer_inc_capa(fb, len); - MEMCPY(fb->ptr + fb->len, newstr, char, len); - fb->len += len; + fbuffer_append_reserved(fb, newstr, len); } } -#ifdef JSON_GENERATOR +/* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */ +static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr) +{ +#if JSON_DEBUG + if (fb->requested < 1) { + rb_bug("fbuffer: unreserved write"); + } + fb->requested--; +#endif + + fb->ptr[fb->len] = chr; + fb->len++; +} + static void fbuffer_append_str(FBuffer *fb, VALUE str) { const char *newstr = StringValuePtr(str); unsigned long len = RSTRING_LEN(str); - RB_GC_GUARD(str); - fbuffer_append(fb, newstr, len); } + +static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat) +{ + const char *newstr = StringValuePtr(str); + unsigned long len = RSTRING_LEN(str); + + fbuffer_inc_capa(fb, repeat * len); + while (repeat) { +#if JSON_DEBUG + fb->requested = len; #endif + fbuffer_append_reserved(fb, newstr, len); + repeat--; + } +} -static void fbuffer_append_char(FBuffer *fb, char newchr) +static inline void fbuffer_append_char(FBuffer *fb, char newchr) { fbuffer_inc_capa(fb, 1); *(fb->ptr + fb->len) = newchr; - fb->len++; + fbuffer_consumed(fb, 1); } -#ifdef JSON_GENERATOR -static void freverse(char *start, char *end) +static inline char *fbuffer_cursor(FBuffer *fb) { - char c; - - while (end > start) { - c = *end, *end-- = *start, *start++ = c; - } + return fb->ptr + fb->len; } -static long fltoa(long number, char *buf) +static inline void fbuffer_advance_to(FBuffer *fb, char *end) { - static char digits[] = "0123456789"; - long sign = number; - char* tmp = buf; - - if (sign < 0) number = -number; - do *tmp++ = digits[number % 10]; while (number /= 10); - if (sign < 0) *tmp++ = '-'; - freverse(buf, tmp - 1); - return tmp - buf; + fbuffer_consumed(fb, (end - fb->ptr) - fb->len); } +/* + * Appends the decimal string representation of \a number into the buffer. + */ static void fbuffer_append_long(FBuffer *fb, long number) { - char buf[20]; - unsigned long len = fltoa(number, buf); - fbuffer_append(fb, buf, len); -} - -static FBuffer *fbuffer_dup(FBuffer *fb) -{ - unsigned long len = fb->len; - FBuffer *result; + /* + * The jeaiii_ultoa() function produces digits left-to-right, + * allowing us to write directly into the buffer, but we don't know + * the number of resulting characters. + * + * We do know, however, that the `number` argument is always in the + * range 0xc000000000000000 to 0x3fffffffffffffff, or, in decimal, + * -4611686018427387904 to 4611686018427387903. The max number of chars + * generated is therefore 20 (including a potential sign character). + */ + + static const int MAX_CHARS_FOR_LONG = 20; + + fbuffer_inc_capa(fb, MAX_CHARS_FOR_LONG); + + if (number < 0) { + fbuffer_append_reserved_char(fb, '-'); + + /* + * Since number is always > LONG_MIN, `-number` will not overflow + * and is always the positive abs() value. + */ + number = -number; + } - result = fbuffer_alloc(len); - fbuffer_append(result, FBUFFER_PAIR(fb)); - return result; + char *end = jeaiii_ultoa(fbuffer_cursor(fb), number); + fbuffer_advance_to(fb, end); } -static VALUE fbuffer_to_s(FBuffer *fb) +static VALUE fbuffer_finalize(FBuffer *fb) { - VALUE result = rb_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb)); - fbuffer_free(fb); - FORCE_UTF8(result); - return result; + if (fb->io) { + fbuffer_flush(fb); + rb_io_flush(fb->io); + return fb->io; + } else { + return rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb)); + } } -#endif -#endif + +#endif // _FBUFFER_H_ diff --git a/ext/json/generator/depend b/ext/json/generator/depend index 42752d1a35..3ba4acfdd2 100644 --- a/ext/json/generator/depend +++ b/ext/json/generator/depend @@ -1,5 +1,5 @@ $(OBJS): $(ruby_headers) -generator.o: generator.c generator.h $(srcdir)/../fbuffer/fbuffer.h +generator.o: generator.c $(srcdir)/../fbuffer/fbuffer.h # AUTOGENERATED DEPENDENCIES START generator.o: $(RUBY_EXTCONF_H) @@ -142,6 +142,7 @@ generator.o: $(hdrdir)/ruby/internal/intern/re.h generator.o: $(hdrdir)/ruby/internal/intern/ruby.h generator.o: $(hdrdir)/ruby/internal/intern/select.h generator.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +generator.o: $(hdrdir)/ruby/internal/intern/set.h generator.o: $(hdrdir)/ruby/internal/intern/signal.h generator.o: $(hdrdir)/ruby/internal/intern/sprintf.h generator.o: $(hdrdir)/ruby/internal/intern/string.h @@ -161,6 +162,7 @@ generator.o: $(hdrdir)/ruby/internal/special_consts.h generator.o: $(hdrdir)/ruby/internal/static_assert.h generator.o: $(hdrdir)/ruby/internal/stdalign.h generator.o: $(hdrdir)/ruby/internal/stdbool.h +generator.o: $(hdrdir)/ruby/internal/stdckdint.h generator.o: $(hdrdir)/ruby/internal/symbol.h generator.o: $(hdrdir)/ruby/internal/value.h generator.o: $(hdrdir)/ruby/internal/value_type.h @@ -176,6 +178,9 @@ generator.o: $(hdrdir)/ruby/ruby.h generator.o: $(hdrdir)/ruby/st.h generator.o: $(hdrdir)/ruby/subst.h generator.o: $(srcdir)/../fbuffer/fbuffer.h +generator.o: $(srcdir)/../json.h +generator.o: $(srcdir)/../simd/simd.h +generator.o: $(srcdir)/../vendor/fpconv.c +generator.o: $(srcdir)/../vendor/jeaiii-ltoa.h generator.o: generator.c -generator.o: generator.h # AUTOGENERATED DEPENDENCIES END diff --git a/ext/json/generator/extconf.rb b/ext/json/generator/extconf.rb index 8627c5f4bd..ee1bbeaba7 100644 --- a/ext/json/generator/extconf.rb +++ b/ext/json/generator/extconf.rb @@ -1,4 +1,16 @@ require 'mkmf' -$defs << "-DJSON_GENERATOR" -create_makefile 'json/ext/generator' +if RUBY_ENGINE == 'truffleruby' + # The pure-Ruby generator is faster on TruffleRuby, so skip compiling the generator extension + File.write('Makefile', dummy_makefile("").join) +else + append_cflags("-std=c99") + $defs << "-DJSON_GENERATOR" + $defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0" + + if enable_config('generator-use-simd', default=!ENV["JSON_DISABLE_SIMD"]) + load __dir__ + "/../simd/conf.rb" + end + + create_makefile 'json/ext/generator' +end diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c index a33df848df..dbba99c455 100644 --- a/ext/json/generator/generator.c +++ b/ext/json/generator/generator.c @@ -1,340 +1,706 @@ +#include "../json.h" #include "../fbuffer/fbuffer.h" -#include "generator.h" +#include "../vendor/fpconv.c" -static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject, - mHash, mArray, +#include <math.h> +#include <ctype.h> + +#include "../simd/simd.h" + +/* ruby api and some helpers */ + +enum duplicate_key_action { + JSON_DEPRECATED = 0, + JSON_IGNORE, + JSON_RAISE, +}; + +typedef struct JSON_Generator_StateStruct { + VALUE indent; + VALUE space; + VALUE space_before; + VALUE object_nl; + VALUE array_nl; + VALUE as_json; + + long max_nesting; + long depth; + long buffer_initial_length; + + enum duplicate_key_action on_duplicate_key; + + bool as_json_single_arg; + bool allow_nan; + bool ascii_only; + bool script_safe; + bool strict; +} JSON_Generator_State; + +static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8; + +static ID i_to_s, i_to_json, i_new, i_encode; +static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan, sym_allow_duplicate_key, + sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json; + + +#define GET_STATE_TO(self, state) \ + TypedData_Get_Struct(self, JSON_Generator_State, &JSON_Generator_State_type, state) + +#define GET_STATE(self) \ + JSON_Generator_State *state; \ + GET_STATE_TO(self, state) + +struct generate_json_data; + +typedef void (*generator_func)(FBuffer *buffer, struct generate_json_data *data, VALUE obj); + +struct generate_json_data { + FBuffer *buffer; + VALUE vstate; + JSON_Generator_State *state; + VALUE obj; + generator_func func; + long depth; +}; + +static SIMD_Implementation simd_impl; + +static VALUE cState_from_state_s(VALUE self, VALUE opts); +static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io); +static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj); #ifdef RUBY_INTEGER_UNIFICATION - mInteger, -#else - mFixnum, mBignum, +static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj); #endif - mFloat, mString, mString_Extend, - mTrueClass, mFalseClass, mNilClass, eGeneratorError, - eNestingError; +static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj); -static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before, - i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only, - i_pack, i_unpack, i_create_id, i_extend, i_key_p, - i_aref, i_send, i_respond_to_p, i_match, i_keys, i_depth, - i_buffer_initial_length, i_dup, i_script_safe, i_escape_slash, i_strict; +static int usascii_encindex, utf8_encindex, binary_encindex; -/* - * Copyright 2001-2004 Unicode, Inc. +NORETURN(static void) raise_generator_error_str(VALUE invalid_object, VALUE str) +{ + rb_enc_associate_index(str, utf8_encindex); + VALUE exc = rb_exc_new_str(eGeneratorError, str); + rb_ivar_set(exc, rb_intern("@invalid_object"), invalid_object); + rb_exc_raise(exc); +} + +#ifdef RBIMPL_ATTR_FORMAT +RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3) +#endif +NORETURN(static void) raise_generator_error(VALUE invalid_object, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + VALUE str = rb_vsprintf(fmt, args); + va_end(args); + raise_generator_error_str(invalid_object, str); +} + +// 0 - single byte char that don't need to be escaped. +// (x | 8) - char that needs to be escaped. +static const unsigned char CHAR_LENGTH_MASK = 7; +static const unsigned char ESCAPE_MASK = 8; + +typedef struct _search_state { + const char *ptr; + const char *end; + const char *cursor; + FBuffer *buffer; + +#ifdef HAVE_SIMD + const char *chunk_base; + const char *chunk_end; + bool has_matches; + +#if defined(HAVE_SIMD_NEON) + uint64_t matches_mask; +#elif defined(HAVE_SIMD_SSE2) + int matches_mask; +#else +#error "Unknown SIMD Implementation." +#endif /* HAVE_SIMD_NEON */ +#endif /* HAVE_SIMD */ +} search_state; + +ALWAYS_INLINE(static) void search_flush(search_state *search) +{ + // Do not remove this conditional without profiling, specifically escape-heavy text. + // escape_UTF8_char_basic will advance search->ptr and search->cursor (effectively a search_flush). + // For back-to-back characters that need to be escaped, specifically for the SIMD code paths, this method + // will be called just before calling escape_UTF8_char_basic. There will be no characters to append for the + // consecutive characters that need to be escaped. While the fbuffer_append is a no-op if + // nothing needs to be flushed, we can save a few memory references with this conditional. + if (search->ptr > search->cursor) { + fbuffer_append(search->buffer, search->cursor, search->ptr - search->cursor); + search->cursor = search->ptr; + } +} + +static const unsigned char escape_table_basic[256] = { + // ASCII Control Characters + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + // ASCII Characters + 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // '"' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static inline unsigned char search_escape_basic(search_state *search) +{ + while (search->ptr < search->end) { + if (RB_UNLIKELY(escape_table_basic[(const unsigned char)*search->ptr])) { + search_flush(search); + return 1; + } else { + search->ptr++; + } + } + search_flush(search); + return 0; +} + +ALWAYS_INLINE(static) void escape_UTF8_char_basic(search_state *search) +{ + const unsigned char ch = (unsigned char)*search->ptr; + switch (ch) { + case '"': fbuffer_append(search->buffer, "\\\"", 2); break; + case '\\': fbuffer_append(search->buffer, "\\\\", 2); break; + case '/': fbuffer_append(search->buffer, "\\/", 2); break; + case '\b': fbuffer_append(search->buffer, "\\b", 2); break; + case '\f': fbuffer_append(search->buffer, "\\f", 2); break; + case '\n': fbuffer_append(search->buffer, "\\n", 2); break; + case '\r': fbuffer_append(search->buffer, "\\r", 2); break; + case '\t': fbuffer_append(search->buffer, "\\t", 2); break; + default: { + const char *hexdig = "0123456789abcdef"; + char scratch[6] = { '\\', 'u', '0', '0', 0, 0 }; + scratch[4] = hexdig[(ch >> 4) & 0xf]; + scratch[5] = hexdig[ch & 0xf]; + fbuffer_append(search->buffer, scratch, 6); + break; + } + } + search->ptr++; + search->cursor = search->ptr; +} + +/* Converts in_string to a JSON string (without the wrapping '"' + * characters) in FBuffer out_buffer. + * + * Character are JSON-escaped according to: * - * Disclaimer + * - Always: ASCII control characters (0x00-0x1F), dquote, and + * backslash. * - * This source code is provided as is by Unicode, Inc. No claims are - * made as to fitness for any particular purpose. No warranties of any - * kind are expressed or implied. The recipient agrees to determine - * applicability of information provided. If this file has been - * purchased on magnetic or optical media from Unicode, Inc., the - * sole remedy for any claim will be exchange of defective media - * within 90 days of receipt. + * - If out_ascii_only: non-ASCII characters (>0x7F) * - * Limitations on Rights to Redistribute This Code + * - If script_safe: forwardslash (/), line separator (U+2028), and + * paragraph separator (U+2029) * - * Unicode, Inc. hereby grants the right to freely use the information - * supplied in this file in the creation of products supporting the - * Unicode Standard, and to make copies of this file in any form - * for internal or external distribution as long as this notice - * remains attached. + * Everything else (should be UTF-8) is just passed through and + * appended to the result. */ -/* - * Index into the table below with the first byte of a UTF-8 sequence to - * get the number of trailing bytes that are supposed to follow it. - * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is - * left as-is for anyone who may want to do such conversion, which was - * allowed in earlier algorithms. - */ -static const char trailingBytesForUTF8[256] = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 -}; -/* - * Magic values subtracted from a buffer value during UTF8 conversion. - * This table contains as many values as there might be trailing bytes - * in a UTF-8 sequence. - */ -static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, - 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; +#if defined(HAVE_SIMD_NEON) +static inline unsigned char search_escape_basic_neon(search_state *search); +#elif defined(HAVE_SIMD_SSE2) +static inline unsigned char search_escape_basic_sse2(search_state *search); +#endif -/* - * Utility routine to tell whether a sequence of bytes is legal UTF-8. - * This must be called with the length pre-determined by the first byte. - * If not calling this from ConvertUTF8to*, then the length can be set by: - * length = trailingBytesForUTF8[*source]+1; - * and the sequence is illegal right away if there aren't that many bytes - * available. - * If presented with a length > 4, this returns 0. The Unicode - * definition of UTF-8 goes up to 4-byte sequences. - */ -static unsigned char isLegalUTF8(const UTF8 *source, unsigned long length) -{ - UTF8 a; - const UTF8 *srcptr = source+length; - switch (length) { - default: return 0; - /* Everything else falls through when "1"... */ - case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; - case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; - case 2: if ((a = (*--srcptr)) > 0xBF) return 0; - - switch (*source) { - /* no fall-through in this inner switch */ - case 0xE0: if (a < 0xA0) return 0; break; - case 0xED: if (a > 0x9F) return 0; break; - case 0xF0: if (a < 0x90) return 0; break; - case 0xF4: if (a > 0x8F) return 0; break; - default: if (a < 0x80) return 0; - } +static inline unsigned char search_escape_basic(search_state *search); - case 1: if (*source >= 0x80 && *source < 0xC2) return 0; +static inline void convert_UTF8_to_JSON(search_state *search) +{ +#ifdef HAVE_SIMD +#if defined(HAVE_SIMD_NEON) + while (search_escape_basic_neon(search)) { + escape_UTF8_char_basic(search); } - if (*source > 0xF4) return 0; - return 1; +#elif defined(HAVE_SIMD_SSE2) + if (simd_impl == SIMD_SSE2) { + while (search_escape_basic_sse2(search)) { + escape_UTF8_char_basic(search); + } + return; + } + while (search_escape_basic(search)) { + escape_UTF8_char_basic(search); + } +#endif +#else + while (search_escape_basic(search)) { + escape_UTF8_char_basic(search); + } +#endif /* HAVE_SIMD */ +} + +static inline void escape_UTF8_char(search_state *search, unsigned char ch_len) +{ + const unsigned char ch = (unsigned char)*search->ptr; + switch (ch_len) { + case 1: { + switch (ch) { + case '"': fbuffer_append(search->buffer, "\\\"", 2); break; + case '\\': fbuffer_append(search->buffer, "\\\\", 2); break; + case '/': fbuffer_append(search->buffer, "\\/", 2); break; + case '\b': fbuffer_append(search->buffer, "\\b", 2); break; + case '\f': fbuffer_append(search->buffer, "\\f", 2); break; + case '\n': fbuffer_append(search->buffer, "\\n", 2); break; + case '\r': fbuffer_append(search->buffer, "\\r", 2); break; + case '\t': fbuffer_append(search->buffer, "\\t", 2); break; + default: { + const char *hexdig = "0123456789abcdef"; + char scratch[6] = { '\\', 'u', '0', '0', 0, 0 }; + scratch[4] = hexdig[(ch >> 4) & 0xf]; + scratch[5] = hexdig[ch & 0xf]; + fbuffer_append(search->buffer, scratch, 6); + break; + } + } + break; + } + case 3: { + if (search->ptr[2] & 1) { + fbuffer_append(search->buffer, "\\u2029", 6); + } else { + fbuffer_append(search->buffer, "\\u2028", 6); + } + break; + } + } + search->cursor = (search->ptr += ch_len); } -/* Escapes the UTF16 character and stores the result in the buffer buf. */ -static void unicode_escape(char *buf, UTF16 character) +#ifdef HAVE_SIMD + +ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len) { - const char *digits = "0123456789abcdef"; + RBIMPL_ASSERT_OR_ASSUME(len < vec_len); + + // Flush the buffer so everything up until the last 'len' characters are unflushed. + search_flush(search); + + FBuffer *buf = search->buffer; + fbuffer_inc_capa(buf, vec_len); + + char *s = (buf->ptr + buf->len); + + // Pad the buffer with dummy characters that won't need escaping. + // This seem wasteful at first sight, but memset of vector length is very fast. + // This is a space as it can be directly represented as an immediate on AArch64. + memset(s, ' ', vec_len); - buf[2] = digits[character >> 12]; - buf[3] = digits[(character >> 8) & 0xf]; - buf[4] = digits[(character >> 4) & 0xf]; - buf[5] = digits[character & 0xf]; + // Optimistically copy the remaining 'len' characters to the output FBuffer. If there are no characters + // to escape, then everything ends up in the correct spot. Otherwise it was convenient temporary storage. + if (vec_len == 16) { + RBIMPL_ASSERT_OR_ASSUME(len >= SIMD_MINIMUM_THRESHOLD); + json_fast_memcpy16(s, search->ptr, len); + } else { + MEMCPY(s, search->ptr, char, len); + } + + return s; } -/* Escapes the UTF16 character and stores the result in the buffer buf, then - * the buffer buf is appended to the FBuffer buffer. */ -static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16 - character) +#ifdef HAVE_SIMD_NEON + +ALWAYS_INLINE(static) unsigned char neon_next_match(search_state *search) { - unicode_escape(buf, character); - fbuffer_append(buffer, buf, 6); + uint64_t mask = search->matches_mask; + uint32_t index = trailing_zeros64(mask) >> 2; + + // It is assumed escape_UTF8_char_basic will only ever increase search->ptr by at most one character. + // If we want to use a similar approach for full escaping we'll need to ensure: + // search->chunk_base + index >= search->ptr + // However, since we know escape_UTF8_char_basic only increases search->ptr by one, if the next match + // is one byte after the previous match then: + // search->chunk_base + index == search->ptr + search->ptr = search->chunk_base + index; + mask &= mask - 1; + search->matches_mask = mask; + search_flush(search); + return 1; } -/* Converts string to a JSON string in FBuffer buffer, where all but the ASCII - * and control characters are JSON escaped. */ -static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char script_safe) +static inline unsigned char search_escape_basic_neon(search_state *search) { - const UTF8 *source = (UTF8 *) RSTRING_PTR(string); - const UTF8 *sourceEnd = source + RSTRING_LEN(string); - char buf[6] = { '\\', 'u' }; - - while (source < sourceEnd) { - UTF32 ch = 0; - unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; - if (source + extraBytesToRead >= sourceEnd) { - rb_raise(rb_path2class("JSON::GeneratorError"), - "partial character in source, but hit end"); - } - if (!isLegalUTF8(source, extraBytesToRead+1)) { - rb_raise(rb_path2class("JSON::GeneratorError"), - "source sequence is illegal/malformed utf-8"); + if (RB_UNLIKELY(search->has_matches)) { + // There are more matches if search->matches_mask > 0. + if (search->matches_mask > 0) { + return neon_next_match(search); + } else { + // neon_next_match will only advance search->ptr up to the last matching character. + // Skip over any characters in the last chunk that occur after the last match. + search->has_matches = false; + search->ptr = search->chunk_end; } - /* - * The cases all fall through. See "Note A" below. - */ - switch (extraBytesToRead) { - case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ - case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ - case 3: ch += *source++; ch <<= 6; - case 2: ch += *source++; ch <<= 6; - case 1: ch += *source++; ch <<= 6; - case 0: ch += *source++; + } + + /* + * The code below implements an SIMD-based algorithm to determine if N bytes at a time + * need to be escaped. + * + * Assume the ptr = "Te\sting!" (the double quotes are included in the string) + * + * The explanation will be limited to the first 8 bytes of the string for simplicity. However + * the vector insructions may work on larger vectors. + * + * First, we load three constants 'lower_bound', 'backslash' and 'dblquote" in vector registers. + * + * lower_bound: [20 20 20 20 20 20 20 20] + * backslash: [5C 5C 5C 5C 5C 5C 5C 5C] + * dblquote: [22 22 22 22 22 22 22 22] + * + * Next we load the first chunk of the ptr: + * [22 54 65 5C 73 74 69 6E] (" T e \ s t i n) + * + * First we check if any byte in chunk is less than 32 (0x20). This returns the following vector + * as no bytes are less than 32 (0x20): + * [0 0 0 0 0 0 0 0] + * + * Next, we check if any byte in chunk is equal to a backslash: + * [0 0 0 FF 0 0 0 0] + * + * Finally we check if any byte in chunk is equal to a double quote: + * [FF 0 0 0 0 0 0 0] + * + * Now we have three vectors where each byte indicates if the corresponding byte in chunk + * needs to be escaped. We combine these vectors with a series of logical OR instructions. + * This is the needs_escape vector and it is equal to: + * [FF 0 0 FF 0 0 0 0] + * + * Next we compute the bitwise AND between each byte and 0x1 and compute the horizontal sum of + * the values in the vector. This computes how many bytes need to be escaped within this chunk. + * + * Finally we compute a mask that indicates which bytes need to be escaped. If the mask is 0 then, + * no bytes need to be escaped and we can continue to the next chunk. If the mask is not 0 then we + * have at least one byte that needs to be escaped. + */ + + if (string_scan_simd_neon(&search->ptr, search->end, &search->matches_mask)) { + search->has_matches = true; + search->chunk_base = search->ptr; + search->chunk_end = search->ptr + sizeof(uint8x16_t); + return neon_next_match(search); + } + + // There are fewer than 16 bytes left. + unsigned long remaining = (search->end - search->ptr); + if (remaining >= SIMD_MINIMUM_THRESHOLD) { + char *s = copy_remaining_bytes(search, sizeof(uint8x16_t), remaining); + + uint64_t mask = compute_chunk_mask_neon(s); + + if (!mask) { + // Nothing to escape, ensure search_flush doesn't do anything by setting + // search->cursor to search->ptr. + fbuffer_consumed(search->buffer, remaining); + search->ptr = search->end; + search->cursor = search->end; + return 0; } - ch -= offsetsFromUTF8[extraBytesToRead]; - - if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ - /* UTF-16 surrogate values are illegal in UTF-32 */ - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { -#if UNI_STRICT_CONVERSION - source -= (extraBytesToRead+1); /* return to the illegal value itself */ - rb_raise(rb_path2class("JSON::GeneratorError"), - "source sequence is illegal/malformed utf-8"); + + search->matches_mask = mask; + search->has_matches = true; + search->chunk_end = search->end; + search->chunk_base = search->ptr; + return neon_next_match(search); + } + + if (search->ptr < search->end) { + return search_escape_basic(search); + } + + search_flush(search); + return 0; +} +#endif /* HAVE_SIMD_NEON */ + +#ifdef HAVE_SIMD_SSE2 + +ALWAYS_INLINE(static) unsigned char sse2_next_match(search_state *search) +{ + int mask = search->matches_mask; + int index = trailing_zeros(mask); + + // It is assumed escape_UTF8_char_basic will only ever increase search->ptr by at most one character. + // If we want to use a similar approach for full escaping we'll need to ensure: + // search->chunk_base + index >= search->ptr + // However, since we know escape_UTF8_char_basic only increases search->ptr by one, if the next match + // is one byte after the previous match then: + // search->chunk_base + index == search->ptr + search->ptr = search->chunk_base + index; + mask &= mask - 1; + search->matches_mask = mask; + search_flush(search); + return 1; +} + +#if defined(__clang__) || defined(__GNUC__) +#define TARGET_SSE2 __attribute__((target("sse2"))) #else - unicode_escape_to_buffer(buffer, buf, UNI_REPLACEMENT_CHAR); +#define TARGET_SSE2 #endif + +ALWAYS_INLINE(static) TARGET_SSE2 unsigned char search_escape_basic_sse2(search_state *search) +{ + if (RB_UNLIKELY(search->has_matches)) { + // There are more matches if search->matches_mask > 0. + if (search->matches_mask > 0) { + return sse2_next_match(search); + } else { + // sse2_next_match will only advance search->ptr up to the last matching character. + // Skip over any characters in the last chunk that occur after the last match. + search->has_matches = false; + if (RB_UNLIKELY(search->chunk_base + sizeof(__m128i) >= search->end)) { + search->ptr = search->end; } else { - /* normal case */ - if (ch >= 0x20 && ch <= 0x7f) { - switch (ch) { - case '\\': - fbuffer_append(buffer, "\\\\", 2); - break; - case '"': - fbuffer_append(buffer, "\\\"", 2); - break; - case '/': - if(script_safe) { - fbuffer_append(buffer, "\\/", 2); - break; - } - default: - fbuffer_append_char(buffer, (char)ch); - break; - } - } else { - switch (ch) { - case '\n': - fbuffer_append(buffer, "\\n", 2); - break; - case '\r': - fbuffer_append(buffer, "\\r", 2); - break; - case '\t': - fbuffer_append(buffer, "\\t", 2); - break; - case '\f': - fbuffer_append(buffer, "\\f", 2); - break; - case '\b': - fbuffer_append(buffer, "\\b", 2); - break; - default: - unicode_escape_to_buffer(buffer, buf, (UTF16) ch); - break; + search->ptr = search->chunk_base + sizeof(__m128i); + } + } + } + + if (string_scan_simd_sse2(&search->ptr, search->end, &search->matches_mask)) { + search->has_matches = true; + search->chunk_base = search->ptr; + search->chunk_end = search->ptr + sizeof(__m128i); + return sse2_next_match(search); + } + + // There are fewer than 16 bytes left. + unsigned long remaining = (search->end - search->ptr); + if (remaining >= SIMD_MINIMUM_THRESHOLD) { + char *s = copy_remaining_bytes(search, sizeof(__m128i), remaining); + + int needs_escape_mask = compute_chunk_mask_sse2(s); + + if (needs_escape_mask == 0) { + // Nothing to escape, ensure search_flush doesn't do anything by setting + // search->cursor to search->ptr. + fbuffer_consumed(search->buffer, remaining); + search->ptr = search->end; + search->cursor = search->end; + return 0; + } + + search->has_matches = true; + search->matches_mask = needs_escape_mask; + search->chunk_base = search->ptr; + return sse2_next_match(search); + } + + if (search->ptr < search->end) { + return search_escape_basic(search); + } + + search_flush(search); + return 0; +} + +#endif /* HAVE_SIMD_SSE2 */ + +#endif /* HAVE_SIMD */ + +static const unsigned char script_safe_escape_table[256] = { + // ASCII Control Characters + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + // ASCII Characters + 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, // '"' and '/' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Continuation byte + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + // First byte of a 2-byte code point + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + // First byte of a 3-byte code point + 3, 3,11, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xE2 is the start of \u2028 and \u2029 + //First byte of a 4+ byte code point + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 9, 9, +}; + +static inline unsigned char search_script_safe_escape(search_state *search) +{ + while (search->ptr < search->end) { + unsigned char ch = (unsigned char)*search->ptr; + unsigned char ch_len = script_safe_escape_table[ch]; + + if (RB_UNLIKELY(ch_len)) { + if (ch_len & ESCAPE_MASK) { + if (RB_UNLIKELY(ch_len == 11)) { + const unsigned char *uptr = (const unsigned char *)search->ptr; + if (!(uptr[1] == 0x80 && (uptr[2] >> 1) == 0x54)) { + search->ptr += 3; + continue; } } + search_flush(search); + return ch_len & CHAR_LENGTH_MASK; + } else { + search->ptr += ch_len; } - } else if (ch > UNI_MAX_UTF16) { -#if UNI_STRICT_CONVERSION - source -= (extraBytesToRead+1); /* return to the start */ - rb_raise(rb_path2class("JSON::GeneratorError"), - "source sequence is illegal/malformed utf8"); -#else - unicode_escape_to_buffer(buffer, buf, UNI_REPLACEMENT_CHAR); -#endif } else { - /* target is a character in range 0xFFFF - 0x10FFFF. */ - ch -= halfBase; - unicode_escape_to_buffer(buffer, buf, (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START)); - unicode_escape_to_buffer(buffer, buf, (UTF16)((ch & halfMask) + UNI_SUR_LOW_START)); + search->ptr++; } } - RB_GC_GUARD(string); -} - -/* Converts string to a JSON string in FBuffer buffer, where only the - * characters required by the JSON standard are JSON escaped. The remaining - * characters (should be UTF8) are just passed through and appended to the - * result. */ -static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char script_safe) -{ - const char *ptr = RSTRING_PTR(string), *p; - unsigned long len = RSTRING_LEN(string), start = 0, end = 0; - const char *escape = NULL; - int escape_len; - unsigned char c; - char buf[6] = { '\\', 'u' }; - int ascii_only = rb_enc_str_asciionly_p(string); - - for (start = 0, end = 0; end < len;) { - p = ptr + end; - c = (unsigned char) *p; - if (c < 0x20) { - switch (c) { - case '\n': - escape = "\\n"; - escape_len = 2; - break; - case '\r': - escape = "\\r"; - escape_len = 2; - break; - case '\t': - escape = "\\t"; - escape_len = 2; - break; - case '\f': - escape = "\\f"; - escape_len = 2; - break; - case '\b': - escape = "\\b"; - escape_len = 2; - break; - default: - unicode_escape(buf, (UTF16) *p); - escape = buf; - escape_len = 6; + search_flush(search); + return 0; +} + +static void convert_UTF8_to_script_safe_JSON(search_state *search) +{ + unsigned char ch_len; + while ((ch_len = search_script_safe_escape(search))) { + escape_UTF8_char(search, ch_len); + } +} + +static const unsigned char ascii_only_escape_table[256] = { + // ASCII Control Characters + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + // ASCII Characters + 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // '"' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Continuation byte + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + // First byte of a 2-byte code point + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + // First byte of a 3-byte code point + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + //First byte of a 4+ byte code point + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 9, 9, +}; + +static inline unsigned char search_ascii_only_escape(search_state *search, const unsigned char escape_table[256]) +{ + while (search->ptr < search->end) { + unsigned char ch = (unsigned char)*search->ptr; + unsigned char ch_len = escape_table[ch]; + + if (RB_UNLIKELY(ch_len)) { + search_flush(search); + return ch_len & CHAR_LENGTH_MASK; + } else { + search->ptr++; + } + } + search_flush(search); + return 0; +} + +static inline void full_escape_UTF8_char(search_state *search, unsigned char ch_len) +{ + const unsigned char ch = (unsigned char)*search->ptr; + switch (ch_len) { + case 1: { + switch (ch) { + case '"': fbuffer_append(search->buffer, "\\\"", 2); break; + case '\\': fbuffer_append(search->buffer, "\\\\", 2); break; + case '/': fbuffer_append(search->buffer, "\\/", 2); break; + case '\b': fbuffer_append(search->buffer, "\\b", 2); break; + case '\f': fbuffer_append(search->buffer, "\\f", 2); break; + case '\n': fbuffer_append(search->buffer, "\\n", 2); break; + case '\r': fbuffer_append(search->buffer, "\\r", 2); break; + case '\t': fbuffer_append(search->buffer, "\\t", 2); break; + default: { + const char *hexdig = "0123456789abcdef"; + char scratch[6] = { '\\', 'u', '0', '0', 0, 0 }; + scratch[4] = hexdig[(ch >> 4) & 0xf]; + scratch[5] = hexdig[ch & 0xf]; + fbuffer_append(search->buffer, scratch, 6); break; + } } - } else { - switch (c) { - case '\\': - escape = "\\\\"; - escape_len = 2; + break; + } + default: { + const char *hexdig = "0123456789abcdef"; + char scratch[12] = { '\\', 'u', 0, 0, 0, 0, '\\', 'u' }; + + uint32_t wchar = 0; + + switch (ch_len) { + case 2: + wchar = ch & 0x1F; break; - case '"': - escape = "\\\""; - escape_len = 2; + case 3: + wchar = ch & 0x0F; break; - case '/': - if(script_safe) { - escape = "\\/"; - escape_len = 2; - break; - } - default: - { - unsigned short clen = 1; - if (!ascii_only) { - clen += trailingBytesForUTF8[c]; - if (end + clen > len) { - rb_raise(rb_path2class("JSON::GeneratorError"), - "partial character in source, but hit end"); - } - - if (script_safe && c == 0xE2) { - unsigned char c2 = (unsigned char) *(p+1); - unsigned char c3 = (unsigned char) *(p+2); - if (c2 == 0x80 && (c3 == 0xA8 || c3 == 0xA9)) { - fbuffer_append(buffer, ptr + start, end - start); - start = end = (end + clen); - if (c3 == 0xA8) { - fbuffer_append(buffer, "\\u2028", 6); - } else { - fbuffer_append(buffer, "\\u2029", 6); - } - continue; - } - } - - if (!isLegalUTF8((UTF8 *) p, clen)) { - rb_raise(rb_path2class("JSON::GeneratorError"), - "source sequence is illegal/malformed utf-8"); - } - } - end += clen; - } - continue; + case 4: + wchar = ch & 0x07; break; } + + for (short i = 1; i < ch_len; i++) { + wchar = (wchar << 6) | (search->ptr[i] & 0x3F); + } + + if (wchar <= 0xFFFF) { + scratch[2] = hexdig[wchar >> 12]; + scratch[3] = hexdig[(wchar >> 8) & 0xf]; + scratch[4] = hexdig[(wchar >> 4) & 0xf]; + scratch[5] = hexdig[wchar & 0xf]; + fbuffer_append(search->buffer, scratch, 6); + } else { + uint16_t hi, lo; + wchar -= 0x10000; + hi = 0xD800 + (uint16_t)(wchar >> 10); + lo = 0xDC00 + (uint16_t)(wchar & 0x3FF); + + scratch[2] = hexdig[hi >> 12]; + scratch[3] = hexdig[(hi >> 8) & 0xf]; + scratch[4] = hexdig[(hi >> 4) & 0xf]; + scratch[5] = hexdig[hi & 0xf]; + + scratch[8] = hexdig[lo >> 12]; + scratch[9] = hexdig[(lo >> 8) & 0xf]; + scratch[10] = hexdig[(lo >> 4) & 0xf]; + scratch[11] = hexdig[lo & 0xf]; + + fbuffer_append(search->buffer, scratch, 12); + } + + break; } - fbuffer_append(buffer, ptr + start, end - start); - fbuffer_append(buffer, escape, escape_len); - start = ++end; - escape = NULL; } - fbuffer_append(buffer, ptr + start, end - start); + search->cursor = (search->ptr += ch_len); } -static char *fstrndup(const char *ptr, unsigned long len) { - char *result; - if (len <= 0) return NULL; - result = ALLOC_N(char, len); - memcpy(result, ptr, len); - return result; +static void convert_UTF8_to_ASCII_only_JSON(search_state *search, const unsigned char escape_table[256]) +{ + unsigned char ch_len; + while ((ch_len = search_ascii_only_escape(search, escape_table))) { + full_escape_UTF8_char(search, ch_len); + } } /* @@ -429,7 +795,9 @@ static char *fstrndup(const char *ptr, unsigned long len) { */ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) { - GENERATE_JSON(object); + rb_check_arity(argc, 0, 1); + VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); + return cState_partial_generate(Vstate, self, generate_json_object, Qfalse); } /* @@ -440,8 +808,11 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) * _state_ is a JSON::State object, that can also be used to configure the * produced JSON string output further. */ -static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) { - GENERATE_JSON(array); +static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 0, 1); + VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); + return cState_partial_generate(Vstate, self, generate_json_array, Qfalse); } #ifdef RUBY_INTEGER_UNIFICATION @@ -452,7 +823,9 @@ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) { */ static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self) { - GENERATE_JSON(integer); + rb_check_arity(argc, 0, 1); + VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); + return cState_partial_generate(Vstate, self, generate_json_integer, Qfalse); } #else @@ -463,7 +836,9 @@ static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self) { - GENERATE_JSON(fixnum); + rb_check_arity(argc, 0, 1); + VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); + return cState_partial_generate(Vstate, self, generate_json_fixnum, Qfalse); } /* @@ -473,7 +848,9 @@ static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self) { - GENERATE_JSON(bignum); + rb_check_arity(argc, 0, 1); + VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); + return cState_partial_generate(Vstate, self, generate_json_bignum, Qfalse); } #endif @@ -484,18 +861,9 @@ static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self) { - GENERATE_JSON(float); -} - -/* - * call-seq: String.included(modul) - * - * Extends _modul_ with the String::Extend module. - */ -static VALUE mString_included_s(VALUE self, VALUE modul) { - VALUE result = rb_funcall(modul, i_extend, 1, mString_Extend); - rb_call_super(1, &modul); - return result; + rb_check_arity(argc, 0, 1); + VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); + return cState_partial_generate(Vstate, self, generate_json_float, Qfalse); } /* @@ -507,52 +875,9 @@ static VALUE mString_included_s(VALUE self, VALUE modul) { */ static VALUE mString_to_json(int argc, VALUE *argv, VALUE self) { - GENERATE_JSON(string); -} - -/* - * call-seq: to_json_raw_object() - * - * This method creates a raw object hash, that can be nested into - * other data structures and will be generated as a raw string. This - * method should be used, if you want to convert raw strings to JSON - * instead of UTF-8 strings, e. g. binary data. - */ -static VALUE mString_to_json_raw_object(VALUE self) -{ - VALUE ary; - VALUE result = rb_hash_new(); - rb_hash_aset(result, rb_funcall(mJSON, i_create_id, 0), rb_class_name(rb_obj_class(self))); - ary = rb_funcall(self, i_unpack, 1, rb_str_new2("C*")); - rb_hash_aset(result, rb_str_new2("raw"), ary); - return result; -} - -/* - * call-seq: to_json_raw(*args) - * - * This method creates a JSON text from the result of a call to - * to_json_raw_object of this String. - */ -static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self) -{ - VALUE obj = mString_to_json_raw_object(self); - Check_Type(obj, T_HASH); - return mHash_to_json(argc, argv, obj); -} - -/* - * call-seq: json_create(o) - * - * Raw Strings are JSON Objects (the raw bytes are stored in an array for the - * key "raw"). The Ruby String can be created by this module method. - */ -static VALUE mString_Extend_json_create(VALUE self, VALUE o) -{ - VALUE ary; - Check_Type(o, T_HASH); - ary = rb_hash_aref(o, rb_str_new2("raw")); - return rb_funcall(ary, i_pack, 1, rb_str_new2("C*")); + rb_check_arity(argc, 0, 1); + VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); + return cState_partial_generate(Vstate, self, generate_json_string, Qfalse); } /* @@ -562,7 +887,8 @@ static VALUE mString_Extend_json_create(VALUE self, VALUE o) */ static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self) { - GENERATE_JSON(true); + rb_check_arity(argc, 0, 1); + return rb_utf8_str_new("true", 4); } /* @@ -572,7 +898,8 @@ static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self) { - GENERATE_JSON(false); + rb_check_arity(argc, 0, 1); + return rb_utf8_str_new("false", 5); } /* @@ -582,7 +909,8 @@ static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self) { - GENERATE_JSON(null); + rb_check_arity(argc, 0, 1); + return rb_utf8_str_new("null", 4); } /* @@ -599,554 +927,641 @@ static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "01", &state); Check_Type(string, T_STRING); state = cState_from_state_s(cState, state); - return cState_partial_generate(state, string); + return cState_partial_generate(state, string, generate_json_string, Qfalse); +} + +static void State_mark(void *ptr) +{ + JSON_Generator_State *state = ptr; + rb_gc_mark_movable(state->indent); + rb_gc_mark_movable(state->space); + rb_gc_mark_movable(state->space_before); + rb_gc_mark_movable(state->object_nl); + rb_gc_mark_movable(state->array_nl); + rb_gc_mark_movable(state->as_json); +} + +static void State_compact(void *ptr) +{ + JSON_Generator_State *state = ptr; + state->indent = rb_gc_location(state->indent); + state->space = rb_gc_location(state->space); + state->space_before = rb_gc_location(state->space_before); + state->object_nl = rb_gc_location(state->object_nl); + state->array_nl = rb_gc_location(state->array_nl); + state->as_json = rb_gc_location(state->as_json); } static void State_free(void *ptr) { JSON_Generator_State *state = ptr; - if (state->indent) ruby_xfree(state->indent); - if (state->space) ruby_xfree(state->space); - if (state->space_before) ruby_xfree(state->space_before); - if (state->object_nl) ruby_xfree(state->object_nl); - if (state->array_nl) ruby_xfree(state->array_nl); - if (state->array_delim) fbuffer_free(state->array_delim); - if (state->object_delim) fbuffer_free(state->object_delim); - if (state->object_delim2) fbuffer_free(state->object_delim2); ruby_xfree(state); } static size_t State_memsize(const void *ptr) { - const JSON_Generator_State *state = ptr; - size_t size = sizeof(*state); - if (state->indent) size += state->indent_len + 1; - if (state->space) size += state->space_len + 1; - if (state->space_before) size += state->space_before_len + 1; - if (state->object_nl) size += state->object_nl_len + 1; - if (state->array_nl) size += state->array_nl_len + 1; - if (state->array_delim) size += FBUFFER_CAPA(state->array_delim); - if (state->object_delim) size += FBUFFER_CAPA(state->object_delim); - if (state->object_delim2) size += FBUFFER_CAPA(state->object_delim2); - return size; -} - -#ifndef HAVE_RB_EXT_RACTOR_SAFE -# undef RUBY_TYPED_FROZEN_SHAREABLE -# define RUBY_TYPED_FROZEN_SHAREABLE 0 -#endif + return sizeof(JSON_Generator_State); +} -#ifdef NEW_TYPEDDATA_WRAPPER static const rb_data_type_t JSON_Generator_State_type = { "JSON/Generator/State", - {NULL, State_free, State_memsize,}, -#ifdef RUBY_TYPED_FREE_IMMEDIATELY + { + .dmark = State_mark, + .dfree = State_free, + .dsize = State_memsize, + .dcompact = State_compact, + }, 0, 0, - RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE, -#endif + RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE, }; -#endif + +static void state_init(JSON_Generator_State *state) +{ + state->max_nesting = 100; + state->buffer_initial_length = FBUFFER_INITIAL_LENGTH_DEFAULT; +} static VALUE cState_s_allocate(VALUE klass) { JSON_Generator_State *state; - return TypedData_Make_Struct(klass, JSON_Generator_State, - &JSON_Generator_State_type, state); + VALUE obj = TypedData_Make_Struct(klass, JSON_Generator_State, &JSON_Generator_State_type, state); + state_init(state); + return obj; } -/* - * call-seq: configure(opts) - * - * Configure this State instance with the Hash _opts_, and return - * itself. - */ -static VALUE cState_configure(VALUE self, VALUE opts) +static void vstate_spill(struct generate_json_data *data) { - VALUE tmp; - GET_STATE(self); - tmp = rb_check_convert_type(opts, T_HASH, "Hash", "to_hash"); - if (NIL_P(tmp)) tmp = rb_convert_type(opts, T_HASH, "Hash", "to_h"); - opts = tmp; - tmp = rb_hash_aref(opts, ID2SYM(i_indent)); - if (RTEST(tmp)) { - unsigned long len; - Check_Type(tmp, T_STRING); - len = RSTRING_LEN(tmp); - state->indent = fstrndup(RSTRING_PTR(tmp), len + 1); - state->indent_len = len; - } - tmp = rb_hash_aref(opts, ID2SYM(i_space)); - if (RTEST(tmp)) { - unsigned long len; - Check_Type(tmp, T_STRING); - len = RSTRING_LEN(tmp); - state->space = fstrndup(RSTRING_PTR(tmp), len + 1); - state->space_len = len; + VALUE vstate = cState_s_allocate(cState); + GET_STATE(vstate); + MEMCPY(state, data->state, JSON_Generator_State, 1); + data->state = state; + data->vstate = vstate; + RB_OBJ_WRITTEN(vstate, Qundef, state->indent); + RB_OBJ_WRITTEN(vstate, Qundef, state->space); + RB_OBJ_WRITTEN(vstate, Qundef, state->space_before); + RB_OBJ_WRITTEN(vstate, Qundef, state->object_nl); + RB_OBJ_WRITTEN(vstate, Qundef, state->array_nl); + RB_OBJ_WRITTEN(vstate, Qundef, state->as_json); +} + +static inline VALUE json_call_to_json(struct generate_json_data *data, VALUE obj) +{ + if (RB_UNLIKELY(!data->vstate)) { + vstate_spill(data); } - tmp = rb_hash_aref(opts, ID2SYM(i_space_before)); - if (RTEST(tmp)) { - unsigned long len; - Check_Type(tmp, T_STRING); - len = RSTRING_LEN(tmp); - state->space_before = fstrndup(RSTRING_PTR(tmp), len + 1); - state->space_before_len = len; + GET_STATE(data->vstate); + state->depth = data->depth; + VALUE tmp = rb_funcall(obj, i_to_json, 1, data->vstate); + // no need to restore state->depth, vstate is just a temporary State + return tmp; +} + +static VALUE +json_call_as_json(JSON_Generator_State *state, VALUE object, VALUE is_key) +{ + VALUE proc_args[2] = {object, is_key}; + return rb_proc_call_with_block(state->as_json, 2, proc_args, Qnil); +} + +static VALUE +convert_string_subclass(VALUE key) +{ + VALUE key_to_s = rb_funcall(key, i_to_s, 0); + + if (RB_UNLIKELY(!RB_TYPE_P(key_to_s, T_STRING))) { + VALUE cname = rb_obj_class(key); + rb_raise(rb_eTypeError, + "can't convert %"PRIsVALUE" to %s (%"PRIsVALUE"#%s gives %"PRIsVALUE")", + cname, "String", cname, "to_s", rb_obj_class(key_to_s)); } - tmp = rb_hash_aref(opts, ID2SYM(i_array_nl)); - if (RTEST(tmp)) { - unsigned long len; - Check_Type(tmp, T_STRING); - len = RSTRING_LEN(tmp); - state->array_nl = fstrndup(RSTRING_PTR(tmp), len + 1); - state->array_nl_len = len; + + return key_to_s; +} + +static bool enc_utf8_compatible_p(int enc_idx) +{ + if (enc_idx == usascii_encindex) return true; + if (enc_idx == utf8_encindex) return true; + return false; +} + +static VALUE encode_json_string_try(VALUE str) +{ + return rb_funcall(str, i_encode, 1, Encoding_UTF_8); +} + +static VALUE encode_json_string_rescue(VALUE str, VALUE exception) +{ + raise_generator_error_str(str, rb_funcall(exception, rb_intern("message"), 0)); + return Qundef; +} + +static inline bool valid_json_string_p(VALUE str) +{ + int coderange = rb_enc_str_coderange(str); + + if (RB_LIKELY(coderange == ENC_CODERANGE_7BIT)) { + return true; } - tmp = rb_hash_aref(opts, ID2SYM(i_object_nl)); - if (RTEST(tmp)) { - unsigned long len; - Check_Type(tmp, T_STRING); - len = RSTRING_LEN(tmp); - state->object_nl = fstrndup(RSTRING_PTR(tmp), len + 1); - state->object_nl_len = len; + + if (RB_LIKELY(coderange == ENC_CODERANGE_VALID)) { + return enc_utf8_compatible_p(RB_ENCODING_GET_INLINED(str)); } - tmp = ID2SYM(i_max_nesting); - state->max_nesting = 100; - if (option_given_p(opts, tmp)) { - VALUE max_nesting = rb_hash_aref(opts, tmp); - if (RTEST(max_nesting)) { - Check_Type(max_nesting, T_FIXNUM); - state->max_nesting = FIX2LONG(max_nesting); - } else { - state->max_nesting = 0; - } + + return false; +} + +static inline VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key) +{ + if (RB_LIKELY(valid_json_string_p(str))) { + return str; } - tmp = ID2SYM(i_depth); - state->depth = 0; - if (option_given_p(opts, tmp)) { - VALUE depth = rb_hash_aref(opts, tmp); - if (RTEST(depth)) { - Check_Type(depth, T_FIXNUM); - state->depth = FIX2LONG(depth); - } else { - state->depth = 0; + + if (!as_json_called && data->state->strict && RTEST(data->state->as_json)) { + VALUE coerced_str = json_call_as_json(data->state, str, Qfalse); + if (coerced_str != str) { + if (RB_TYPE_P(coerced_str, T_STRING)) { + if (!valid_json_string_p(coerced_str)) { + raise_generator_error(str, "source sequence is illegal/malformed utf-8"); + } + } else { + // as_json could return another type than T_STRING + if (is_key) { + raise_generator_error(coerced_str, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(coerced_str)); + } + } + + return coerced_str; } } - tmp = ID2SYM(i_buffer_initial_length); - if (option_given_p(opts, tmp)) { - VALUE buffer_initial_length = rb_hash_aref(opts, tmp); - if (RTEST(buffer_initial_length)) { - long initial_length; - Check_Type(buffer_initial_length, T_FIXNUM); - initial_length = FIX2LONG(buffer_initial_length); - if (initial_length > 0) state->buffer_initial_length = initial_length; + + if (RB_ENCODING_GET_INLINED(str) == binary_encindex) { + VALUE utf8_string = rb_enc_associate_index(rb_str_dup(str), utf8_encindex); + switch (rb_enc_str_coderange(utf8_string)) { + case ENC_CODERANGE_7BIT: + return utf8_string; + case ENC_CODERANGE_VALID: + // For historical reason, we silently reinterpret binary strings as UTF-8 if it would work. + // TODO: Raise in 3.0.0 + rb_warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0"); + return utf8_string; + break; } } - tmp = rb_hash_aref(opts, ID2SYM(i_allow_nan)); - state->allow_nan = RTEST(tmp); - tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only)); - state->ascii_only = RTEST(tmp); - tmp = rb_hash_aref(opts, ID2SYM(i_script_safe)); - state->script_safe = RTEST(tmp); - if (!state->script_safe) { - tmp = rb_hash_aref(opts, ID2SYM(i_escape_slash)); - state->script_safe = RTEST(tmp); - } - tmp = rb_hash_aref(opts, ID2SYM(i_strict)); - state->strict = RTEST(tmp); - return self; + + return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str); } -static void set_state_ivars(VALUE hash, VALUE state) +static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { - VALUE ivars = rb_obj_instance_variables(state); - int i = 0; - for (i = 0; i < RARRAY_LEN(ivars); i++) { - VALUE key = rb_funcall(rb_ary_entry(ivars, i), i_to_s, 0); - long key_len = RSTRING_LEN(key); - VALUE value = rb_iv_get(state, StringValueCStr(key)); - rb_hash_aset(hash, rb_str_intern(rb_str_substr(key, 1, key_len - 1)), value); + fbuffer_append_char(buffer, '"'); + + long len; + search_state search; + search.buffer = buffer; + RSTRING_GETMEM(obj, search.ptr, len); + search.cursor = search.ptr; + search.end = search.ptr + len; + +#ifdef HAVE_SIMD + search.matches_mask = 0; + search.has_matches = false; + search.chunk_base = NULL; + search.chunk_end = NULL; +#endif /* HAVE_SIMD */ + + switch (rb_enc_str_coderange(obj)) { + case ENC_CODERANGE_7BIT: + case ENC_CODERANGE_VALID: + if (RB_UNLIKELY(data->state->ascii_only)) { + convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table); + } else if (RB_UNLIKELY(data->state->script_safe)) { + convert_UTF8_to_script_safe_JSON(&search); + } else { + convert_UTF8_to_JSON(&search); + } + break; + default: + raise_generator_error(obj, "source sequence is illegal/malformed utf-8"); + break; } + fbuffer_append_char(buffer, '"'); } -/* - * call-seq: to_h - * - * Returns the configuration instance variables as a hash, that can be - * passed to the configure method. - */ -static VALUE cState_to_h(VALUE self) +static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { - VALUE result = rb_hash_new(); - GET_STATE(self); - set_state_ivars(result, self); - rb_hash_aset(result, ID2SYM(i_indent), rb_str_new(state->indent, state->indent_len)); - rb_hash_aset(result, ID2SYM(i_space), rb_str_new(state->space, state->space_len)); - rb_hash_aset(result, ID2SYM(i_space_before), rb_str_new(state->space_before, state->space_before_len)); - rb_hash_aset(result, ID2SYM(i_object_nl), rb_str_new(state->object_nl, state->object_nl_len)); - rb_hash_aset(result, ID2SYM(i_array_nl), rb_str_new(state->array_nl, state->array_nl_len)); - rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse); - rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse); - rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting)); - rb_hash_aset(result, ID2SYM(i_script_safe), state->script_safe ? Qtrue : Qfalse); - rb_hash_aset(result, ID2SYM(i_strict), state->strict ? Qtrue : Qfalse); - rb_hash_aset(result, ID2SYM(i_depth), LONG2FIX(state->depth)); - rb_hash_aset(result, ID2SYM(i_buffer_initial_length), LONG2FIX(state->buffer_initial_length)); - return result; + obj = ensure_valid_encoding(data, obj, false, false); + raw_generate_json_string(buffer, data, obj); } -/* -* call-seq: [](name) -* -* Returns the value returned by method +name+. -*/ -static VALUE cState_aref(VALUE self, VALUE name) -{ - name = rb_funcall(name, i_to_s, 0); - if (RTEST(rb_funcall(self, i_respond_to_p, 1, name))) { - return rb_funcall(self, i_send, 1, name); - } else { - return rb_attr_get(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name))); - } -} +struct hash_foreach_arg { + VALUE hash; + struct generate_json_data *data; + int first_key_type; + bool first; + bool mixed_keys_encountered; +}; -/* -* call-seq: []=(name, value) -* -* Sets the attribute name to value. -*/ -static VALUE cState_aset(VALUE self, VALUE name, VALUE value) +NOINLINE(static) void +json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg) { - VALUE name_writer; + if (arg->mixed_keys_encountered) { + return; + } + arg->mixed_keys_encountered = true; - name = rb_funcall(name, i_to_s, 0); - name_writer = rb_str_cat2(rb_str_dup(name), "="); - if (RTEST(rb_funcall(self, i_respond_to_p, 1, name_writer))) { - return rb_funcall(self, i_send, 2, name_writer, value); - } else { - rb_ivar_set(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name)), value); + JSON_Generator_State *state = arg->data->state; + if (state->on_duplicate_key != JSON_IGNORE) { + VALUE do_raise = state->on_duplicate_key == JSON_RAISE ? Qtrue : Qfalse; + rb_funcall(mJSON, rb_intern("on_mixed_keys_hash"), 2, arg->hash, do_raise); } - return Qnil; } -struct hash_foreach_arg { - FBuffer *buffer; - JSON_Generator_State *state; - VALUE Vstate; - int iter; -}; - static int json_object_i(VALUE key, VALUE val, VALUE _arg) { struct hash_foreach_arg *arg = (struct hash_foreach_arg *)_arg; - FBuffer *buffer = arg->buffer; - JSON_Generator_State *state = arg->state; - VALUE Vstate = arg->Vstate; - - char *object_nl = state->object_nl; - long object_nl_len = state->object_nl_len; - char *indent = state->indent; - long indent_len = state->indent_len; - char *delim = FBUFFER_PTR(state->object_delim); - long delim_len = FBUFFER_LEN(state->object_delim); - char *delim2 = FBUFFER_PTR(state->object_delim2); - long delim2_len = FBUFFER_LEN(state->object_delim2); - long depth = state->depth; - int j; - VALUE klass, key_to_s; - - if (arg->iter > 0) fbuffer_append(buffer, delim, delim_len); - if (object_nl) { - fbuffer_append(buffer, object_nl, object_nl_len); + struct generate_json_data *data = arg->data; + + FBuffer *buffer = data->buffer; + JSON_Generator_State *state = data->state; + + long depth = data->depth; + int key_type = rb_type(key); + + if (arg->first) { + arg->first = false; + arg->first_key_type = key_type; } - if (indent) { - for (j = 0; j < depth; j++) { - fbuffer_append(buffer, indent, indent_len); - } + else { + fbuffer_append_char(buffer, ','); + } + + if (RB_UNLIKELY(data->state->object_nl)) { + fbuffer_append_str(buffer, data->state->object_nl); + } + if (RB_UNLIKELY(data->state->indent)) { + fbuffer_append_str_repeat(buffer, data->state->indent, depth); + } + + VALUE key_to_s; + bool as_json_called = false; + + start: + switch (key_type) { + case T_STRING: + if (RB_UNLIKELY(arg->first_key_type != T_STRING)) { + json_inspect_hash_with_mixed_keys(arg); + } + + if (RB_LIKELY(RBASIC_CLASS(key) == rb_cString)) { + key_to_s = key; + } else { + key_to_s = convert_string_subclass(key); + } + break; + case T_SYMBOL: + if (RB_UNLIKELY(arg->first_key_type != T_SYMBOL)) { + json_inspect_hash_with_mixed_keys(arg); + } + + key_to_s = rb_sym2str(key); + break; + default: + if (data->state->strict) { + if (RTEST(data->state->as_json) && !as_json_called) { + key = json_call_as_json(data->state, key, Qtrue); + key_type = rb_type(key); + as_json_called = true; + goto start; + } else { + raise_generator_error(key, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(key)); + } + } + key_to_s = rb_convert_type(key, T_STRING, "String", "to_s"); + break; } - klass = CLASS_OF(key); - if (klass == rb_cString) { - key_to_s = key; - } else if (klass == rb_cSymbol) { - key_to_s = rb_sym2str(key); + key_to_s = ensure_valid_encoding(data, key_to_s, as_json_called, true); + + if (RB_LIKELY(RBASIC_CLASS(key_to_s) == rb_cString)) { + raw_generate_json_string(buffer, data, key_to_s); } else { - key_to_s = rb_funcall(key, i_to_s, 0); + generate_json(buffer, data, key_to_s); } - Check_Type(key_to_s, T_STRING); - generate_json(buffer, Vstate, state, key_to_s); - fbuffer_append(buffer, delim2, delim2_len); - generate_json(buffer, Vstate, state, val); + if (RB_UNLIKELY(state->space_before)) fbuffer_append_str(buffer, data->state->space_before); + fbuffer_append_char(buffer, ':'); + if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, data->state->space); + generate_json(buffer, data, val); - arg->iter++; return ST_CONTINUE; } -static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) +static inline long increase_depth(struct generate_json_data *data) +{ + JSON_Generator_State *state = data->state; + long depth = ++data->depth; + if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) { + rb_raise(eNestingError, "nesting of %ld is too deep. Did you try to serialize objects with circular references?", --data->depth); + } + return depth; +} + +static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { - char *object_nl = state->object_nl; - long object_nl_len = state->object_nl_len; - char *indent = state->indent; - long indent_len = state->indent_len; - long max_nesting = state->max_nesting; - long depth = ++state->depth; - int j; - struct hash_foreach_arg arg; + long depth = increase_depth(data); - if (max_nesting != 0 && depth > max_nesting) { - fbuffer_free(buffer); - rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth); + if (RHASH_SIZE(obj) == 0) { + fbuffer_append(buffer, "{}", 2); + --data->depth; + return; } + fbuffer_append_char(buffer, '{'); - arg.buffer = buffer; - arg.state = state; - arg.Vstate = Vstate; - arg.iter = 0; + struct hash_foreach_arg arg = { + .hash = obj, + .data = data, + .first = true, + }; rb_hash_foreach(obj, json_object_i, (VALUE)&arg); - depth = --state->depth; - if (object_nl) { - fbuffer_append(buffer, object_nl, object_nl_len); - if (indent) { - for (j = 0; j < depth; j++) { - fbuffer_append(buffer, indent, indent_len); - } + depth = --data->depth; + if (RB_UNLIKELY(data->state->object_nl)) { + fbuffer_append_str(buffer, data->state->object_nl); + if (RB_UNLIKELY(data->state->indent)) { + fbuffer_append_str_repeat(buffer, data->state->indent, depth); } } fbuffer_append_char(buffer, '}'); } -static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) -{ - char *array_nl = state->array_nl; - long array_nl_len = state->array_nl_len; - char *indent = state->indent; - long indent_len = state->indent_len; - long max_nesting = state->max_nesting; - char *delim = FBUFFER_PTR(state->array_delim); - long delim_len = FBUFFER_LEN(state->array_delim); - long depth = ++state->depth; - int i, j; - if (max_nesting != 0 && depth > max_nesting) { - fbuffer_free(buffer); - rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth); +static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + long depth = increase_depth(data); + + if (RARRAY_LEN(obj) == 0) { + fbuffer_append(buffer, "[]", 2); + --data->depth; + return; } + fbuffer_append_char(buffer, '['); - if (array_nl) fbuffer_append(buffer, array_nl, array_nl_len); - for(i = 0; i < RARRAY_LEN(obj); i++) { - if (i > 0) fbuffer_append(buffer, delim, delim_len); - if (indent) { - for (j = 0; j < depth; j++) { - fbuffer_append(buffer, indent, indent_len); - } + if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl); + for (int i = 0; i < RARRAY_LEN(obj); i++) { + if (i > 0) { + fbuffer_append_char(buffer, ','); + if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl); + } + if (RB_UNLIKELY(data->state->indent)) { + fbuffer_append_str_repeat(buffer, data->state->indent, depth); } - generate_json(buffer, Vstate, state, rb_ary_entry(obj, i)); + generate_json(buffer, data, RARRAY_AREF(obj, i)); } - state->depth = --depth; - if (array_nl) { - fbuffer_append(buffer, array_nl, array_nl_len); - if (indent) { - for (j = 0; j < depth; j++) { - fbuffer_append(buffer, indent, indent_len); - } + data->depth = --depth; + if (RB_UNLIKELY(data->state->array_nl)) { + fbuffer_append_str(buffer, data->state->array_nl); + if (RB_UNLIKELY(data->state->indent)) { + fbuffer_append_str_repeat(buffer, data->state->indent, depth); } } fbuffer_append_char(buffer, ']'); } -#ifdef HAVE_RUBY_ENCODING_H -static int enc_utf8_compatible_p(rb_encoding *enc) +static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { - if (enc == rb_usascii_encoding()) return 1; - if (enc == rb_utf8_encoding()) return 1; - return 0; + VALUE tmp; + if (rb_respond_to(obj, i_to_json)) { + tmp = json_call_to_json(data, obj); + Check_Type(tmp, T_STRING); + fbuffer_append_str(buffer, tmp); + } else { + tmp = rb_funcall(obj, i_to_s, 0); + Check_Type(tmp, T_STRING); + generate_json_string(buffer, data, tmp); + } } -#endif -static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) +static inline void generate_json_symbol(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { - fbuffer_append_char(buffer, '"'); -#ifdef HAVE_RUBY_ENCODING_H - if (!enc_utf8_compatible_p(rb_enc_get(obj))) { - obj = rb_str_export_to_enc(obj, rb_utf8_encoding()); - } -#endif - if (state->ascii_only) { - convert_UTF8_to_JSON_ASCII(buffer, obj, state->script_safe); + if (data->state->strict) { + generate_json_string(buffer, data, rb_sym2str(obj)); } else { - convert_UTF8_to_JSON(buffer, obj, state->script_safe); + generate_json_fallback(buffer, data, obj); } - fbuffer_append_char(buffer, '"'); } -static void generate_json_null(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) +static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { fbuffer_append(buffer, "null", 4); } -static void generate_json_false(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) +static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { fbuffer_append(buffer, "false", 5); } -static void generate_json_true(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) +static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { fbuffer_append(buffer, "true", 4); } -static void generate_json_fixnum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) +static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { fbuffer_append_long(buffer, FIX2LONG(obj)); } -static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) +static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { VALUE tmp = rb_funcall(obj, i_to_s, 0); fbuffer_append_str(buffer, tmp); } #ifdef RUBY_INTEGER_UNIFICATION -static void generate_json_integer(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) +static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { if (FIXNUM_P(obj)) - generate_json_fixnum(buffer, Vstate, state, obj); + generate_json_fixnum(buffer, data, obj); else - generate_json_bignum(buffer, Vstate, state, obj); + generate_json_bignum(buffer, data, obj); } #endif -static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) + +static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { double value = RFLOAT_VALUE(obj); - char allow_nan = state->allow_nan; - VALUE tmp = rb_funcall(obj, i_to_s, 0); - if (!allow_nan) { - if (isinf(value)) { - fbuffer_free(buffer); - rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp)); - } else if (isnan(value)) { - fbuffer_free(buffer); - rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp)); + char allow_nan = data->state->allow_nan; + if (isinf(value) || isnan(value)) { + /* for NaN and Infinity values we either raise an error or rely on Float#to_s. */ + if (!allow_nan) { + if (data->state->strict && data->state->as_json) { + VALUE casted_obj = json_call_as_json(data->state, obj, Qfalse); + if (casted_obj != obj) { + increase_depth(data); + generate_json(buffer, data, casted_obj); + data->depth--; + return; + } + } + raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", rb_funcall(obj, i_to_s, 0)); } + + VALUE tmp = rb_funcall(obj, i_to_s, 0); + fbuffer_append_str(buffer, tmp); + return; } - fbuffer_append_str(buffer, tmp); + + /* This implementation writes directly into the buffer. We reserve + * the 32 characters that fpconv_dtoa states as its maximum. + */ + fbuffer_inc_capa(buffer, 32); + char* d = buffer->ptr + buffer->len; + int len = fpconv_dtoa(value, d); + /* fpconv_dtoa converts a float to its shortest string representation, + * but it adds a ".0" if this is a plain integer. + */ + fbuffer_consumed(buffer, len); } -static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) +static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { - VALUE tmp; - VALUE klass = CLASS_OF(obj); - if (klass == rb_cHash) { - generate_json_object(buffer, Vstate, state, obj); - } else if (klass == rb_cArray) { - generate_json_array(buffer, Vstate, state, obj); - } else if (klass == rb_cString) { - generate_json_string(buffer, Vstate, state, obj); - } else if (obj == Qnil) { - generate_json_null(buffer, Vstate, state, obj); + VALUE fragment = RSTRUCT_GET(obj, 0); + Check_Type(fragment, T_STRING); + fbuffer_append_str(buffer, fragment); +} + +static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + bool as_json_called = false; +start: + if (obj == Qnil) { + generate_json_null(buffer, data, obj); } else if (obj == Qfalse) { - generate_json_false(buffer, Vstate, state, obj); + generate_json_false(buffer, data, obj); } else if (obj == Qtrue) { - generate_json_true(buffer, Vstate, state, obj); - } else if (FIXNUM_P(obj)) { - generate_json_fixnum(buffer, Vstate, state, obj); - } else if (RB_TYPE_P(obj, T_BIGNUM)) { - generate_json_bignum(buffer, Vstate, state, obj); - } else if (klass == rb_cFloat) { - generate_json_float(buffer, Vstate, state, obj); - } else if (state->strict) { - rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(CLASS_OF(obj))); - } else if (rb_respond_to(obj, i_to_json)) { - tmp = rb_funcall(obj, i_to_json, 1, Vstate); - Check_Type(tmp, T_STRING); - fbuffer_append_str(buffer, tmp); + generate_json_true(buffer, data, obj); + } else if (RB_SPECIAL_CONST_P(obj)) { + if (RB_FIXNUM_P(obj)) { + generate_json_fixnum(buffer, data, obj); + } else if (RB_FLONUM_P(obj)) { + generate_json_float(buffer, data, obj); + } else if (RB_STATIC_SYM_P(obj)) { + generate_json_symbol(buffer, data, obj); + } else { + goto general; + } } else { - tmp = rb_funcall(obj, i_to_s, 0); - Check_Type(tmp, T_STRING); - generate_json_string(buffer, Vstate, state, tmp); + VALUE klass = RBASIC_CLASS(obj); + switch (RB_BUILTIN_TYPE(obj)) { + case T_BIGNUM: + generate_json_bignum(buffer, data, obj); + break; + case T_HASH: + if (klass != rb_cHash) goto general; + generate_json_object(buffer, data, obj); + break; + case T_ARRAY: + if (klass != rb_cArray) goto general; + generate_json_array(buffer, data, obj); + break; + case T_STRING: + if (klass != rb_cString) goto general; + + if (RB_LIKELY(valid_json_string_p(obj))) { + raw_generate_json_string(buffer, data, obj); + } else if (as_json_called) { + raise_generator_error(obj, "source sequence is illegal/malformed utf-8"); + } else { + obj = ensure_valid_encoding(data, obj, false, false); + as_json_called = true; + goto start; + } + break; + case T_SYMBOL: + generate_json_symbol(buffer, data, obj); + break; + case T_FLOAT: + if (klass != rb_cFloat) goto general; + generate_json_float(buffer, data, obj); + break; + case T_STRUCT: + if (klass != cFragment) goto general; + generate_json_fragment(buffer, data, obj); + break; + default: + general: + if (data->state->strict) { + if (RTEST(data->state->as_json) && !as_json_called) { + obj = json_call_as_json(data->state, obj, Qfalse); + as_json_called = true; + goto start; + } else { + raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj)); + } + } else { + generate_json_fallback(buffer, data, obj); + } + } } } -static FBuffer *cState_prepare_buffer(VALUE self) +static VALUE generate_json_try(VALUE d) { - FBuffer *buffer; - GET_STATE(self); - buffer = fbuffer_alloc(state->buffer_initial_length); + struct generate_json_data *data = (struct generate_json_data *)d; - if (state->object_delim) { - fbuffer_clear(state->object_delim); - } else { - state->object_delim = fbuffer_alloc(16); - } - fbuffer_append_char(state->object_delim, ','); - if (state->object_delim2) { - fbuffer_clear(state->object_delim2); - } else { - state->object_delim2 = fbuffer_alloc(16); - } - if (state->space_before) fbuffer_append(state->object_delim2, state->space_before, state->space_before_len); - fbuffer_append_char(state->object_delim2, ':'); - if (state->space) fbuffer_append(state->object_delim2, state->space, state->space_len); + data->func(data->buffer, data, data->obj); - if (state->array_delim) { - fbuffer_clear(state->array_delim); - } else { - state->array_delim = fbuffer_alloc(16); - } - fbuffer_append_char(state->array_delim, ','); - if (state->array_nl) fbuffer_append(state->array_delim, state->array_nl, state->array_nl_len); - return buffer; + return fbuffer_finalize(data->buffer); } -static VALUE cState_partial_generate(VALUE self, VALUE obj) +static VALUE generate_json_ensure(VALUE d) { - FBuffer *buffer = cState_prepare_buffer(self); - GET_STATE(self); - generate_json(buffer, self, state, obj); - return fbuffer_to_s(buffer); + struct generate_json_data *data = (struct generate_json_data *)d; + fbuffer_free(data->buffer); + + return Qundef; } -/* - * call-seq: generate(obj) +static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, VALUE io) +{ + GET_STATE(self); + + char stack_buffer[FBUFFER_STACK_SIZE]; + FBuffer buffer = { + .io = RTEST(io) ? io : Qfalse, + }; + fbuffer_stack_init(&buffer, state->buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE); + + struct generate_json_data data = { + .buffer = &buffer, + .vstate = Qfalse, // don't use self as it may be frozen and its depth is mutated when calling to_json + .state = state, + .depth = state->depth, + .obj = obj, + .func = func + }; + return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data); +} + +/* call-seq: + * generate(obj) -> String + * generate(obj, anIO) -> anIO * * Generates a valid JSON document from object +obj+ and returns the * result. If no valid JSON document can be created this method raises a * GeneratorError exception. */ -static VALUE cState_generate(VALUE self, VALUE obj) +static VALUE cState_generate(int argc, VALUE *argv, VALUE self) { - VALUE result = cState_partial_generate(self, obj); - GET_STATE(self); - (void)state; - return result; + rb_check_arity(argc, 1, 2); + VALUE obj = argv[0]; + VALUE io = argc > 1 ? argv[1] : Qnil; + return cState_partial_generate(self, obj, generate_json, io); } -/* - * call-seq: new(opts = {}) - * - * Instantiates a new State object, configured by _opts_. - * - * _opts_ can have the following keys: - * - * * *indent*: a string used to indent levels (default: ''), - * * *space*: a string that is put after, a : or , delimiter (default: ''), - * * *space_before*: a string that is put before a : pair delimiter (default: ''), - * * *object_nl*: a string that is put at the end of a JSON object (default: ''), - * * *array_nl*: a string that is put at the end of a JSON array (default: ''), - * * *allow_nan*: true if NaN, Infinity, and -Infinity should be - * generated, otherwise an exception is thrown, if these values are - * encountered. This options defaults to false. - * * *ascii_only*: true if only ASCII characters should be generated. This - * option defaults to false. - * * *buffer_initial_length*: sets the initial length of the generator's - * internal buffer. - */ static VALUE cState_initialize(int argc, VALUE *argv, VALUE self) { - VALUE opts; - GET_STATE(self); - state->max_nesting = 100; - state->buffer_initial_length = FBUFFER_INITIAL_LENGTH_DEFAULT; - rb_scan_args(argc, argv, "01", &opts); - if (!NIL_P(opts)) cState_configure(self, opts); + rb_warn("The json gem extension was loaded with the stdlib ruby code. You should upgrade rubygems with `gem update --system`"); return self; } @@ -1166,14 +1581,12 @@ static VALUE cState_init_copy(VALUE obj, VALUE orig) if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State"); MEMCPY(objState, origState, JSON_Generator_State, 1); - objState->indent = fstrndup(origState->indent, origState->indent_len); - objState->space = fstrndup(origState->space, origState->space_len); - objState->space_before = fstrndup(origState->space_before, origState->space_before_len); - objState->object_nl = fstrndup(origState->object_nl, origState->object_nl_len); - objState->array_nl = fstrndup(origState->array_nl, origState->array_nl_len); - if (origState->array_delim) objState->array_delim = fbuffer_dup(origState->array_delim); - if (origState->object_delim) objState->object_delim = fbuffer_dup(origState->object_delim); - if (origState->object_delim2) objState->object_delim2 = fbuffer_dup(origState->object_delim2); + objState->indent = origState->indent; + objState->space = origState->space; + objState->space_before = origState->space_before; + objState->object_nl = origState->object_nl; + objState->array_nl = origState->array_nl; + objState->as_json = origState->as_json; return obj; } @@ -1203,7 +1616,18 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts) static VALUE cState_indent(VALUE self) { GET_STATE(self); - return state->indent ? rb_str_new(state->indent, state->indent_len) : rb_str_new2(""); + return state->indent ? state->indent : rb_str_freeze(rb_utf8_str_new("", 0)); +} + +static VALUE string_config(VALUE config) +{ + if (RTEST(config)) { + Check_Type(config, T_STRING); + if (RSTRING_LEN(config)) { + return rb_str_new_frozen(config); + } + } + return Qfalse; } /* @@ -1213,21 +1637,9 @@ static VALUE cState_indent(VALUE self) */ static VALUE cState_indent_set(VALUE self, VALUE indent) { - unsigned long len; + rb_check_frozen(self); GET_STATE(self); - Check_Type(indent, T_STRING); - len = RSTRING_LEN(indent); - if (len == 0) { - if (state->indent) { - ruby_xfree(state->indent); - state->indent = NULL; - state->indent_len = 0; - } - } else { - if (state->indent) ruby_xfree(state->indent); - state->indent = fstrndup(RSTRING_PTR(indent), len); - state->indent_len = len; - } + RB_OBJ_WRITE(self, &state->indent, string_config(indent)); return Qnil; } @@ -1240,7 +1652,7 @@ static VALUE cState_indent_set(VALUE self, VALUE indent) static VALUE cState_space(VALUE self) { GET_STATE(self); - return state->space ? rb_str_new(state->space, state->space_len) : rb_str_new2(""); + return state->space ? state->space : rb_str_freeze(rb_utf8_str_new("", 0)); } /* @@ -1251,21 +1663,9 @@ static VALUE cState_space(VALUE self) */ static VALUE cState_space_set(VALUE self, VALUE space) { - unsigned long len; + rb_check_frozen(self); GET_STATE(self); - Check_Type(space, T_STRING); - len = RSTRING_LEN(space); - if (len == 0) { - if (state->space) { - ruby_xfree(state->space); - state->space = NULL; - state->space_len = 0; - } - } else { - if (state->space) ruby_xfree(state->space); - state->space = fstrndup(RSTRING_PTR(space), len); - state->space_len = len; - } + RB_OBJ_WRITE(self, &state->space, string_config(space)); return Qnil; } @@ -1277,7 +1677,7 @@ static VALUE cState_space_set(VALUE self, VALUE space) static VALUE cState_space_before(VALUE self) { GET_STATE(self); - return state->space_before ? rb_str_new(state->space_before, state->space_before_len) : rb_str_new2(""); + return state->space_before ? state->space_before : rb_str_freeze(rb_utf8_str_new("", 0)); } /* @@ -1287,21 +1687,9 @@ static VALUE cState_space_before(VALUE self) */ static VALUE cState_space_before_set(VALUE self, VALUE space_before) { - unsigned long len; + rb_check_frozen(self); GET_STATE(self); - Check_Type(space_before, T_STRING); - len = RSTRING_LEN(space_before); - if (len == 0) { - if (state->space_before) { - ruby_xfree(state->space_before); - state->space_before = NULL; - state->space_before_len = 0; - } - } else { - if (state->space_before) ruby_xfree(state->space_before); - state->space_before = fstrndup(RSTRING_PTR(space_before), len); - state->space_before_len = len; - } + RB_OBJ_WRITE(self, &state->space_before, string_config(space_before)); return Qnil; } @@ -1314,7 +1702,7 @@ static VALUE cState_space_before_set(VALUE self, VALUE space_before) static VALUE cState_object_nl(VALUE self) { GET_STATE(self); - return state->object_nl ? rb_str_new(state->object_nl, state->object_nl_len) : rb_str_new2(""); + return state->object_nl ? state->object_nl : rb_str_freeze(rb_utf8_str_new("", 0)); } /* @@ -1325,20 +1713,9 @@ static VALUE cState_object_nl(VALUE self) */ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl) { - unsigned long len; + rb_check_frozen(self); GET_STATE(self); - Check_Type(object_nl, T_STRING); - len = RSTRING_LEN(object_nl); - if (len == 0) { - if (state->object_nl) { - ruby_xfree(state->object_nl); - state->object_nl = NULL; - } - } else { - if (state->object_nl) ruby_xfree(state->object_nl); - state->object_nl = fstrndup(RSTRING_PTR(object_nl), len); - state->object_nl_len = len; - } + RB_OBJ_WRITE(self, &state->object_nl, string_config(object_nl)); return Qnil; } @@ -1350,7 +1727,7 @@ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl) static VALUE cState_array_nl(VALUE self) { GET_STATE(self); - return state->array_nl ? rb_str_new(state->array_nl, state->array_nl_len) : rb_str_new2(""); + return state->array_nl ? state->array_nl : rb_str_freeze(rb_utf8_str_new("", 0)); } /* @@ -1360,23 +1737,35 @@ static VALUE cState_array_nl(VALUE self) */ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl) { - unsigned long len; + rb_check_frozen(self); GET_STATE(self); - Check_Type(array_nl, T_STRING); - len = RSTRING_LEN(array_nl); - if (len == 0) { - if (state->array_nl) { - ruby_xfree(state->array_nl); - state->array_nl = NULL; - } - } else { - if (state->array_nl) ruby_xfree(state->array_nl); - state->array_nl = fstrndup(RSTRING_PTR(array_nl), len); - state->array_nl_len = len; - } + RB_OBJ_WRITE(self, &state->array_nl, string_config(array_nl)); return Qnil; } +/* + * call-seq: as_json() + * + * This string is put at the end of a line that holds a JSON array. + */ +static VALUE cState_as_json(VALUE self) +{ + GET_STATE(self); + return state->as_json; +} + +/* + * call-seq: as_json=(as_json) + * + * This string is put at the end of a line that holds a JSON array. + */ +static VALUE cState_as_json_set(VALUE self, VALUE as_json) +{ + rb_check_frozen(self); + GET_STATE(self); + RB_OBJ_WRITE(self, &state->as_json, rb_convert_type(as_json, T_DATA, "Proc", "to_proc")); + return Qnil; +} /* * call-seq: check_circular? @@ -1402,6 +1791,11 @@ static VALUE cState_max_nesting(VALUE self) return LONG2FIX(state->max_nesting); } +static long long_config(VALUE num) +{ + return RTEST(num) ? FIX2LONG(num) : 0; +} + /* * call-seq: max_nesting=(depth) * @@ -1410,9 +1804,10 @@ static VALUE cState_max_nesting(VALUE self) */ static VALUE cState_max_nesting_set(VALUE self, VALUE depth) { + rb_check_frozen(self); GET_STATE(self); - Check_Type(depth, T_FIXNUM); - return state->max_nesting = FIX2LONG(depth); + state->max_nesting = long_config(depth); + return Qnil; } /* @@ -1435,6 +1830,7 @@ static VALUE cState_script_safe(VALUE self) */ static VALUE cState_script_safe_set(VALUE self, VALUE enable) { + rb_check_frozen(self); GET_STATE(self); state->script_safe = RTEST(enable); return Qnil; @@ -1466,6 +1862,7 @@ static VALUE cState_strict(VALUE self) */ static VALUE cState_strict_set(VALUE self, VALUE enable) { + rb_check_frozen(self); GET_STATE(self); state->strict = RTEST(enable); return Qnil; @@ -1484,6 +1881,19 @@ static VALUE cState_allow_nan_p(VALUE self) } /* + * call-seq: allow_nan=(enable) + * + * This sets whether or not to serialize NaN, Infinity, and -Infinity + */ +static VALUE cState_allow_nan_set(VALUE self, VALUE enable) +{ + rb_check_frozen(self); + GET_STATE(self); + state->allow_nan = RTEST(enable); + return Qnil; +} + +/* * call-seq: ascii_only? * * Returns true, if only ASCII characters should be generated. Otherwise @@ -1496,6 +1906,32 @@ static VALUE cState_ascii_only_p(VALUE self) } /* + * call-seq: ascii_only=(enable) + * + * This sets whether only ASCII characters should be generated. + */ +static VALUE cState_ascii_only_set(VALUE self, VALUE enable) +{ + rb_check_frozen(self); + GET_STATE(self); + state->ascii_only = RTEST(enable); + return Qnil; +} + +static VALUE cState_allow_duplicate_key_p(VALUE self) +{ + GET_STATE(self); + switch (state->on_duplicate_key) { + case JSON_IGNORE: + return Qtrue; + case JSON_DEPRECATED: + return Qnil; + default: + return Qfalse; + } +} + +/* * call-seq: depth * * This integer returns the current depth of data structure nesting. @@ -1514,9 +1950,9 @@ static VALUE cState_depth(VALUE self) */ static VALUE cState_depth_set(VALUE self, VALUE depth) { + rb_check_frozen(self); GET_STATE(self); - Check_Type(depth, T_FIXNUM); - state->depth = FIX2LONG(depth); + state->depth = long_config(depth); return Qnil; } @@ -1531,6 +1967,15 @@ static VALUE cState_buffer_initial_length(VALUE self) return LONG2FIX(state->buffer_initial_length); } +static void buffer_initial_length_set(JSON_Generator_State *state, VALUE buffer_initial_length) +{ + Check_Type(buffer_initial_length, T_FIXNUM); + long initial_length = FIX2LONG(buffer_initial_length); + if (initial_length > 0) { + state->buffer_initial_length = initial_length; + } +} + /* * call-seq: buffer_initial_length=(length) * @@ -1539,16 +1984,102 @@ static VALUE cState_buffer_initial_length(VALUE self) */ static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_length) { - long initial_length; + rb_check_frozen(self); GET_STATE(self); - Check_Type(buffer_initial_length, T_FIXNUM); - initial_length = FIX2LONG(buffer_initial_length); - if (initial_length > 0) { - state->buffer_initial_length = initial_length; - } + buffer_initial_length_set(state, buffer_initial_length); return Qnil; } +struct configure_state_data { + JSON_Generator_State *state; + VALUE vstate; // Ruby object that owns the state, or Qfalse if stack-allocated +}; + +static inline void state_write_value(struct configure_state_data *data, VALUE *field, VALUE value) +{ + if (RTEST(data->vstate)) { + RB_OBJ_WRITE(data->vstate, field, value); + } else { + *field = value; + } +} + +static int configure_state_i(VALUE key, VALUE val, VALUE _arg) +{ + struct configure_state_data *data = (struct configure_state_data *)_arg; + JSON_Generator_State *state = data->state; + + if (key == sym_indent) { state_write_value(data, &state->indent, string_config(val)); } + else if (key == sym_space) { state_write_value(data, &state->space, string_config(val)); } + else if (key == sym_space_before) { state_write_value(data, &state->space_before, string_config(val)); } + else if (key == sym_object_nl) { state_write_value(data, &state->object_nl, string_config(val)); } + else if (key == sym_array_nl) { state_write_value(data, &state->array_nl, string_config(val)); } + else if (key == sym_max_nesting) { state->max_nesting = long_config(val); } + else if (key == sym_allow_nan) { state->allow_nan = RTEST(val); } + else if (key == sym_ascii_only) { state->ascii_only = RTEST(val); } + else if (key == sym_depth) { state->depth = long_config(val); } + else if (key == sym_buffer_initial_length) { buffer_initial_length_set(state, val); } + else if (key == sym_script_safe) { state->script_safe = RTEST(val); } + else if (key == sym_escape_slash) { state->script_safe = RTEST(val); } + else if (key == sym_strict) { state->strict = RTEST(val); } + else if (key == sym_allow_duplicate_key) { state->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; } + else if (key == sym_as_json) { + VALUE proc = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse; + state->as_json_single_arg = proc && rb_proc_arity(proc) == 1; + state_write_value(data, &state->as_json, proc); + } + return ST_CONTINUE; +} + +static void configure_state(JSON_Generator_State *state, VALUE vstate, VALUE config) +{ + if (!RTEST(config)) return; + + Check_Type(config, T_HASH); + + if (!RHASH_SIZE(config)) return; + + struct configure_state_data data = { + .state = state, + .vstate = vstate + }; + + // We assume in most cases few keys are set so it's faster to go over + // the provided keys than to check all possible keys. + rb_hash_foreach(config, configure_state_i, (VALUE)&data); +} + +static VALUE cState_configure(VALUE self, VALUE opts) +{ + rb_check_frozen(self); + GET_STATE(self); + configure_state(state, self, opts); + return self; +} + +static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io) +{ + JSON_Generator_State state = {0}; + state_init(&state); + configure_state(&state, Qfalse, opts); + + char stack_buffer[FBUFFER_STACK_SIZE]; + FBuffer buffer = { + .io = RTEST(io) ? io : Qfalse, + }; + fbuffer_stack_init(&buffer, state.buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE); + + struct generate_json_data data = { + .buffer = &buffer, + .vstate = Qfalse, + .state = &state, + .depth = state.depth, + .obj = obj, + .func = generate_json, + }; + return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data); +} + /* * */ @@ -1562,18 +2093,26 @@ void Init_generator(void) rb_require("json/common"); mJSON = rb_define_module("JSON"); - mExt = rb_define_module_under(mJSON, "Ext"); - mGenerator = rb_define_module_under(mExt, "Generator"); + rb_global_variable(&cFragment); + cFragment = rb_const_get(mJSON, rb_intern("Fragment")); + + VALUE mExt = rb_define_module_under(mJSON, "Ext"); + VALUE mGenerator = rb_define_module_under(mExt, "Generator"); + + rb_global_variable(&eGeneratorError); eGeneratorError = rb_path2class("JSON::GeneratorError"); + + rb_global_variable(&eNestingError); eNestingError = rb_path2class("JSON::NestingError"); - rb_gc_register_mark_object(eGeneratorError); - rb_gc_register_mark_object(eNestingError); cState = rb_define_class_under(mGenerator, "State", rb_cObject); rb_define_alloc_func(cState, cState_s_allocate); rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1); rb_define_method(cState, "initialize", cState_initialize, -1); + rb_define_alias(cState, "initialize", "initialize"); // avoid method redefinition warnings + rb_define_private_method(cState, "_configure", cState_configure, 1); + rb_define_method(cState, "initialize_copy", cState_init_copy, 1); rb_define_method(cState, "indent", cState_indent, 0); rb_define_method(cState, "indent=", cState_indent_set, 1); @@ -1585,6 +2124,8 @@ void Init_generator(void) rb_define_method(cState, "object_nl=", cState_object_nl_set, 1); rb_define_method(cState, "array_nl", cState_array_nl, 0); rb_define_method(cState, "array_nl=", cState_array_nl_set, 1); + rb_define_method(cState, "as_json", cState_as_json, 0); + rb_define_method(cState, "as_json=", cState_as_json_set, 1); rb_define_method(cState, "max_nesting", cState_max_nesting, 0); rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1); rb_define_method(cState, "script_safe", cState_script_safe, 0); @@ -1598,76 +2139,84 @@ void Init_generator(void) rb_define_method(cState, "strict=", cState_strict_set, 1); rb_define_method(cState, "check_circular?", cState_check_circular_p, 0); rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0); + rb_define_method(cState, "allow_nan=", cState_allow_nan_set, 1); rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0); + rb_define_method(cState, "ascii_only=", cState_ascii_only_set, 1); rb_define_method(cState, "depth", cState_depth, 0); rb_define_method(cState, "depth=", cState_depth_set, 1); rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0); rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1); - rb_define_method(cState, "configure", cState_configure, 1); - rb_define_alias(cState, "merge", "configure"); - rb_define_method(cState, "to_h", cState_to_h, 0); - rb_define_alias(cState, "to_hash", "to_h"); - rb_define_method(cState, "[]", cState_aref, 1); - rb_define_method(cState, "[]=", cState_aset, 2); - rb_define_method(cState, "generate", cState_generate, 1); - - mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods"); - mObject = rb_define_module_under(mGeneratorMethods, "Object"); + rb_define_method(cState, "generate", cState_generate, -1); + + rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0); + + rb_define_singleton_method(cState, "generate", cState_m_generate, 3); + + VALUE mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods"); + + VALUE mObject = rb_define_module_under(mGeneratorMethods, "Object"); rb_define_method(mObject, "to_json", mObject_to_json, -1); - mHash = rb_define_module_under(mGeneratorMethods, "Hash"); + + VALUE mHash = rb_define_module_under(mGeneratorMethods, "Hash"); rb_define_method(mHash, "to_json", mHash_to_json, -1); - mArray = rb_define_module_under(mGeneratorMethods, "Array"); + + VALUE mArray = rb_define_module_under(mGeneratorMethods, "Array"); rb_define_method(mArray, "to_json", mArray_to_json, -1); + #ifdef RUBY_INTEGER_UNIFICATION - mInteger = rb_define_module_under(mGeneratorMethods, "Integer"); + VALUE mInteger = rb_define_module_under(mGeneratorMethods, "Integer"); rb_define_method(mInteger, "to_json", mInteger_to_json, -1); #else - mFixnum = rb_define_module_under(mGeneratorMethods, "Fixnum"); + VALUE mFixnum = rb_define_module_under(mGeneratorMethods, "Fixnum"); rb_define_method(mFixnum, "to_json", mFixnum_to_json, -1); - mBignum = rb_define_module_under(mGeneratorMethods, "Bignum"); + + VALUE mBignum = rb_define_module_under(mGeneratorMethods, "Bignum"); rb_define_method(mBignum, "to_json", mBignum_to_json, -1); #endif - mFloat = rb_define_module_under(mGeneratorMethods, "Float"); + VALUE mFloat = rb_define_module_under(mGeneratorMethods, "Float"); rb_define_method(mFloat, "to_json", mFloat_to_json, -1); - mString = rb_define_module_under(mGeneratorMethods, "String"); - rb_define_singleton_method(mString, "included", mString_included_s, 1); + + VALUE mString = rb_define_module_under(mGeneratorMethods, "String"); rb_define_method(mString, "to_json", mString_to_json, -1); - rb_define_method(mString, "to_json_raw", mString_to_json_raw, -1); - rb_define_method(mString, "to_json_raw_object", mString_to_json_raw_object, 0); - mString_Extend = rb_define_module_under(mString, "Extend"); - rb_define_method(mString_Extend, "json_create", mString_Extend_json_create, 1); - mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass"); + + VALUE mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass"); rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1); - mFalseClass = rb_define_module_under(mGeneratorMethods, "FalseClass"); + + VALUE mFalseClass = rb_define_module_under(mGeneratorMethods, "FalseClass"); rb_define_method(mFalseClass, "to_json", mFalseClass_to_json, -1); - mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass"); + + VALUE mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass"); rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1); + rb_global_variable(&Encoding_UTF_8); + Encoding_UTF_8 = rb_const_get(rb_path2class("Encoding"), rb_intern("UTF_8")); + i_to_s = rb_intern("to_s"); i_to_json = rb_intern("to_json"); i_new = rb_intern("new"); - i_indent = rb_intern("indent"); - i_space = rb_intern("space"); - i_space_before = rb_intern("space_before"); - i_object_nl = rb_intern("object_nl"); - i_array_nl = rb_intern("array_nl"); - i_max_nesting = rb_intern("max_nesting"); - i_script_safe = rb_intern("script_safe"); - i_escape_slash = rb_intern("escape_slash"); - i_strict = rb_intern("strict"); - i_allow_nan = rb_intern("allow_nan"); - i_ascii_only = rb_intern("ascii_only"); - i_depth = rb_intern("depth"); - i_buffer_initial_length = rb_intern("buffer_initial_length"); - i_pack = rb_intern("pack"); - i_unpack = rb_intern("unpack"); - i_create_id = rb_intern("create_id"); - i_extend = rb_intern("extend"); - i_key_p = rb_intern("key?"); - i_aref = rb_intern("[]"); - i_send = rb_intern("__send__"); - i_respond_to_p = rb_intern("respond_to?"); - i_match = rb_intern("match"); - i_keys = rb_intern("keys"); - i_dup = rb_intern("dup"); + i_encode = rb_intern("encode"); + + sym_indent = ID2SYM(rb_intern("indent")); + sym_space = ID2SYM(rb_intern("space")); + sym_space_before = ID2SYM(rb_intern("space_before")); + sym_object_nl = ID2SYM(rb_intern("object_nl")); + sym_array_nl = ID2SYM(rb_intern("array_nl")); + sym_max_nesting = ID2SYM(rb_intern("max_nesting")); + sym_allow_nan = ID2SYM(rb_intern("allow_nan")); + sym_ascii_only = ID2SYM(rb_intern("ascii_only")); + sym_depth = ID2SYM(rb_intern("depth")); + sym_buffer_initial_length = ID2SYM(rb_intern("buffer_initial_length")); + sym_script_safe = ID2SYM(rb_intern("script_safe")); + sym_escape_slash = ID2SYM(rb_intern("escape_slash")); + sym_strict = ID2SYM(rb_intern("strict")); + sym_as_json = ID2SYM(rb_intern("as_json")); + sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key")); + + usascii_encindex = rb_usascii_encindex(); + utf8_encindex = rb_utf8_encindex(); + binary_encindex = rb_ascii8bit_encindex(); + + rb_require("json/ext/generator/state"); + + simd_impl = find_simd_implementation(); } diff --git a/ext/json/generator/generator.h b/ext/json/generator/generator.h deleted file mode 100644 index 1a736b84dd..0000000000 --- a/ext/json/generator/generator.h +++ /dev/null @@ -1,177 +0,0 @@ -#ifndef _GENERATOR_H_ -#define _GENERATOR_H_ - -#include <math.h> -#include <ctype.h> - -#include "ruby.h" - -#ifdef HAVE_RUBY_RE_H -#include "ruby/re.h" -#else -#include "re.h" -#endif - -#ifndef rb_intern_str -#define rb_intern_str(string) SYM2ID(rb_str_intern(string)) -#endif - -#ifndef rb_obj_instance_variables -#define rb_obj_instance_variables(object) rb_funcall(object, rb_intern("instance_variables"), 0) -#endif - -#define option_given_p(opts, key) RTEST(rb_funcall(opts, i_key_p, 1, key)) - -/* unicode definitions */ - -#define UNI_STRICT_CONVERSION 1 - -typedef unsigned long UTF32; /* at least 32 bits */ -typedef unsigned short UTF16; /* at least 16 bits */ -typedef unsigned char UTF8; /* typically 8 bits */ - -#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD -#define UNI_MAX_BMP (UTF32)0x0000FFFF -#define UNI_MAX_UTF16 (UTF32)0x0010FFFF -#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF -#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF - -#define UNI_SUR_HIGH_START (UTF32)0xD800 -#define UNI_SUR_HIGH_END (UTF32)0xDBFF -#define UNI_SUR_LOW_START (UTF32)0xDC00 -#define UNI_SUR_LOW_END (UTF32)0xDFFF - -static const int halfShift = 10; /* used for shifting by 10 bits */ - -static const UTF32 halfBase = 0x0010000UL; -static const UTF32 halfMask = 0x3FFUL; - -static unsigned char isLegalUTF8(const UTF8 *source, unsigned long length); -static void unicode_escape(char *buf, UTF16 character); -static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16 character); -static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char script_safe); -static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char script_safe); -static char *fstrndup(const char *ptr, unsigned long len); - -/* ruby api and some helpers */ - -typedef struct JSON_Generator_StateStruct { - char *indent; - long indent_len; - char *space; - long space_len; - char *space_before; - long space_before_len; - char *object_nl; - long object_nl_len; - char *array_nl; - long array_nl_len; - FBuffer *array_delim; - FBuffer *object_delim; - FBuffer *object_delim2; - long max_nesting; - char allow_nan; - char ascii_only; - char script_safe; - char strict; - long depth; - long buffer_initial_length; -} JSON_Generator_State; - -#define GET_STATE_TO(self, state) \ - TypedData_Get_Struct(self, JSON_Generator_State, &JSON_Generator_State_type, state) - -#define GET_STATE(self) \ - JSON_Generator_State *state; \ - GET_STATE_TO(self, state) - -#define GENERATE_JSON(type) \ - FBuffer *buffer; \ - VALUE Vstate; \ - JSON_Generator_State *state; \ - \ - rb_scan_args(argc, argv, "01", &Vstate); \ - Vstate = cState_from_state_s(cState, Vstate); \ - TypedData_Get_Struct(Vstate, JSON_Generator_State, &JSON_Generator_State_type, state); \ - buffer = cState_prepare_buffer(Vstate); \ - generate_json_##type(buffer, Vstate, state, self); \ - return fbuffer_to_s(buffer) - -static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self); -static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self); -#ifdef RUBY_INTEGER_UNIFICATION -static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self); -#else -static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self); -static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self); -#endif -static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self); -static VALUE mString_included_s(VALUE self, VALUE modul); -static VALUE mString_to_json(int argc, VALUE *argv, VALUE self); -static VALUE mString_to_json_raw_object(VALUE self); -static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self); -static VALUE mString_Extend_json_create(VALUE self, VALUE o); -static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self); -static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self); -static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self); -static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self); -static void State_free(void *state); -static VALUE cState_s_allocate(VALUE klass); -static VALUE cState_configure(VALUE self, VALUE opts); -static VALUE cState_to_h(VALUE self); -static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); -static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); -static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); -static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); -static void generate_json_null(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); -static void generate_json_false(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); -static void generate_json_true(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); -#ifdef RUBY_INTEGER_UNIFICATION -static void generate_json_integer(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); -#endif -static void generate_json_fixnum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); -static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); -static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); -static VALUE cState_partial_generate(VALUE self, VALUE obj); -static VALUE cState_generate(VALUE self, VALUE obj); -static VALUE cState_initialize(int argc, VALUE *argv, VALUE self); -static VALUE cState_from_state_s(VALUE self, VALUE opts); -static VALUE cState_indent(VALUE self); -static VALUE cState_indent_set(VALUE self, VALUE indent); -static VALUE cState_space(VALUE self); -static VALUE cState_space_set(VALUE self, VALUE space); -static VALUE cState_space_before(VALUE self); -static VALUE cState_space_before_set(VALUE self, VALUE space_before); -static VALUE cState_object_nl(VALUE self); -static VALUE cState_object_nl_set(VALUE self, VALUE object_nl); -static VALUE cState_array_nl(VALUE self); -static VALUE cState_array_nl_set(VALUE self, VALUE array_nl); -static VALUE cState_max_nesting(VALUE self); -static VALUE cState_max_nesting_set(VALUE self, VALUE depth); -static VALUE cState_allow_nan_p(VALUE self); -static VALUE cState_ascii_only_p(VALUE self); -static VALUE cState_depth(VALUE self); -static VALUE cState_depth_set(VALUE self, VALUE depth); -static VALUE cState_script_safe(VALUE self); -static VALUE cState_script_safe_set(VALUE self, VALUE depth); -static VALUE cState_strict(VALUE self); -static VALUE cState_strict_set(VALUE self, VALUE strict); -static FBuffer *cState_prepare_buffer(VALUE self); -#ifndef ZALLOC -#define ZALLOC(type) ((type *)ruby_zalloc(sizeof(type))) -static inline void *ruby_zalloc(size_t n) -{ - void *p = ruby_xmalloc(n); - memset(p, 0, n); - return p; -} -#endif -#ifdef TypedData_Make_Struct -static const rb_data_type_t JSON_Generator_State_type; -#define NEW_TYPEDDATA_WRAPPER 1 -#else -#define TypedData_Make_Struct(klass, type, ignore, json) Data_Make_Struct(klass, type, NULL, State_free, json) -#define TypedData_Get_Struct(self, JSON_Generator_State, ignore, json) Data_Get_Struct(self, JSON_Generator_State, json) -#endif - -#endif diff --git a/ext/json/json.gemspec b/ext/json/json.gemspec index 64d0c81391..5575731025 100644 --- a/ext/json/json.gemspec +++ b/ext/json/json.gemspec @@ -1,68 +1,62 @@ +# frozen_string_literal: true + version = File.foreach(File.join(__dir__, "lib/json/version.rb")) do |line| /^\s*VERSION\s*=\s*'(.*)'/ =~ line and break $1 end rescue nil -Gem::Specification.new do |s| +spec = Gem::Specification.new do |s| + java_ext = Gem::Platform === s.platform && s.platform =~ 'java' || RUBY_ENGINE == 'jruby' + s.name = "json" s.version = version s.summary = "JSON Implementation for Ruby" - s.description = "This is a JSON implementation as a Ruby extension in C." + s.homepage = "https://github.com/ruby/json" + s.metadata = { + 'bug_tracker_uri' => 'https://github.com/ruby/json/issues', + 'changelog_uri' => 'https://github.com/ruby/json/blob/master/CHANGES.md', + 'documentation_uri' => 'https://docs.ruby-lang.org/en/master/JSON.html', + 'homepage_uri' => s.homepage, + 'source_code_uri' => 'https://github.com/ruby/json', + } + + s.required_ruby_version = Gem::Requirement.new(">= 2.7") + + if java_ext + s.description = "A JSON implementation as a JRuby extension." + s.author = "Daniel Luz" + s.email = "dev+ruby@mernen.com" + else + s.description = "This is a JSON implementation as a Ruby extension in C." + s.authors = ["Florian Frank"] + s.email = "flori@ping.de" + end + s.licenses = ["Ruby"] - s.authors = ["Florian Frank"] - s.email = "flori@ping.de" - s.extensions = ["ext/json/ext/generator/extconf.rb", "ext/json/ext/parser/extconf.rb", "ext/json/extconf.rb"] s.extra_rdoc_files = ["README.md"] s.rdoc_options = ["--title", "JSON implementation for Ruby", "--main", "README.md"] + s.files = [ "CHANGES.md", - "LICENSE", + "COPYING", + "BSDL", + "LEGAL", "README.md", - "ext/json/ext/fbuffer/fbuffer.h", - "ext/json/ext/generator/depend", - "ext/json/ext/generator/extconf.rb", - "ext/json/ext/generator/generator.c", - "ext/json/ext/generator/generator.h", - "ext/json/ext/parser/depend", - "ext/json/ext/parser/extconf.rb", - "ext/json/ext/parser/parser.c", - "ext/json/ext/parser/parser.h", - "ext/json/ext/parser/parser.rl", - "ext/json/extconf.rb", "json.gemspec", - "lib/json.rb", - "lib/json/add/bigdecimal.rb", - "lib/json/add/complex.rb", - "lib/json/add/core.rb", - "lib/json/add/date.rb", - "lib/json/add/date_time.rb", - "lib/json/add/exception.rb", - "lib/json/add/ostruct.rb", - "lib/json/add/range.rb", - "lib/json/add/rational.rb", - "lib/json/add/regexp.rb", - "lib/json/add/set.rb", - "lib/json/add/struct.rb", - "lib/json/add/symbol.rb", - "lib/json/add/time.rb", - "lib/json/common.rb", - "lib/json/ext.rb", - "lib/json/generic_object.rb", - "lib/json/pure.rb", - "lib/json/pure/generator.rb", - "lib/json/pure/parser.rb", - "lib/json/version.rb", - ] - s.homepage = "https://flori.github.io/json" - s.metadata = { - 'bug_tracker_uri' => 'https://github.com/flori/json/issues', - 'changelog_uri' => 'https://github.com/flori/json/blob/master/CHANGES.md', - 'documentation_uri' => 'https://flori.github.io/json/doc/index.html', - 'homepage_uri' => s.homepage, - 'source_code_uri' => 'https://github.com/flori/json', - 'wiki_uri' => 'https://github.com/flori/json/wiki' - } + ] + Dir.glob("lib/**/*.rb", base: File.expand_path("..", __FILE__)) + + if java_ext + s.platform = 'java' + s.files += Dir["lib/json/ext/**/*.jar"] + else + s.extensions = Dir["ext/json/**/extconf.rb"] + s.files += Dir["ext/json/**/*.{c,h,rb}"] + end +end - s.required_ruby_version = Gem::Requirement.new(">= 2.3") +if RUBY_ENGINE == 'jruby' && $0 == __FILE__ + Gem::Builder.new(spec).build +else + spec end diff --git a/ext/json/json.h b/ext/json/json.h new file mode 100644 index 0000000000..9379d7ae7f --- /dev/null +++ b/ext/json/json.h @@ -0,0 +1,101 @@ +#ifndef _JSON_H_ +#define _JSON_H_ + +#include "ruby.h" +#include "ruby/encoding.h" +#include <stdint.h> + +#ifndef RBIMPL_ASSERT_OR_ASSUME +# define RBIMPL_ASSERT_OR_ASSUME(x) +#endif + +#if defined(RUBY_DEBUG) && RUBY_DEBUG +# define JSON_ASSERT RUBY_ASSERT +#else +# ifdef JSON_DEBUG +# include <assert.h> +# define JSON_ASSERT(x) assert(x) +# else +# define JSON_ASSERT(x) +# endif +#endif + +/* shims */ + +#if SIZEOF_UINT64_T == SIZEOF_LONG_LONG +# define INT64T2NUM(x) LL2NUM(x) +# define UINT64T2NUM(x) ULL2NUM(x) +#elif SIZEOF_UINT64_T == SIZEOF_LONG +# define INT64T2NUM(x) LONG2NUM(x) +# define UINT64T2NUM(x) ULONG2NUM(x) +#else +# error No uint64_t conversion +#endif + +/* This is the fallback definition from Ruby 3.4 */ +#ifndef RBIMPL_STDBOOL_H +#if defined(__cplusplus) +# if defined(HAVE_STDBOOL_H) && (__cplusplus >= 201103L) +# include <cstdbool> +# endif +#elif defined(HAVE_STDBOOL_H) +# include <stdbool.h> +#elif !defined(HAVE__BOOL) +typedef unsigned char _Bool; +# define bool _Bool +# define true ((_Bool)+1) +# define false ((_Bool)+0) +# define __bool_true_false_are_defined +#endif +#endif + +#ifndef HAVE_RB_EXT_RACTOR_SAFE +# undef RUBY_TYPED_FROZEN_SHAREABLE +# define RUBY_TYPED_FROZEN_SHAREABLE 0 +#endif + +#ifndef NORETURN +#define NORETURN(x) x +#endif + +#ifndef NOINLINE +#if defined(__has_attribute) && __has_attribute(noinline) +#define NOINLINE(x) __attribute__((noinline)) x +#else +#define NOINLINE(x) x +#endif +#endif + +#ifndef ALWAYS_INLINE +#if defined(__has_attribute) && __has_attribute(always_inline) +#define ALWAYS_INLINE(x) inline __attribute__((always_inline)) x +#else +#define ALWAYS_INLINE(x) inline x +#endif +#endif + +#ifndef RB_UNLIKELY +#define RB_UNLIKELY(expr) expr +#endif + +#ifndef RB_LIKELY +#define RB_LIKELY(expr) expr +#endif + +#ifndef MAYBE_UNUSED +# define MAYBE_UNUSED(x) x +#endif + +#ifdef RUBY_DEBUG +#ifndef JSON_DEBUG +#define JSON_DEBUG RUBY_DEBUG +#endif +#endif + +#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ && INTPTR_MAX == INT64_MAX +#define JSON_CPU_LITTLE_ENDIAN_64BITS 1 +#else +#define JSON_CPU_LITTLE_ENDIAN_64BITS 0 +#endif + +#endif // _JSON_H_ diff --git a/ext/json/lib/json.rb b/ext/json/lib/json.rb index 807488ffef..2f6db44227 100644 --- a/ext/json/lib/json.rb +++ b/ext/json/lib/json.rb @@ -1,4 +1,4 @@ -#frozen_string_literal: false +# frozen_string_literal: true require 'json/common' ## @@ -6,6 +6,15 @@ require 'json/common' # # \JSON is a lightweight data-interchange format. # +# \JSON is easy for us humans to read and write, +# and equally simple for machines to read (parse) and write (generate). +# +# \JSON is language-independent, making it an ideal interchange format +# for applications in differing programming languages +# and on differing operating systems. +# +# == \JSON Values +# # A \JSON value is one of the following: # - Double-quoted text: <tt>"foo"</tt>. # - Number: +1+, +1.0+, +2.0e2+. @@ -127,6 +136,24 @@ require 'json/common' # # --- # +# Option +allow_duplicate_key+ specifies whether duplicate keys in objects +# should be ignored or cause an error to be raised: +# +# When not specified: +# # The last value is used and a deprecation warning emitted. +# JSON.parse('{"a": 1, "a":2}') => {"a" => 2} +# # warning: detected duplicate keys in JSON object. +# # This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true` +# +# When set to `+true+` +# # The last value is used. +# JSON.parse('{"a": 1, "a":2}') => {"a" => 2} +# +# When set to `+false+`, the future default: +# JSON.parse('{"a": 1, "a":2}') => duplicate key at line 1 column 1 (JSON::ParserError) +# +# --- +# # Option +allow_nan+ (boolean) specifies whether to allow # NaN, Infinity, and MinusInfinity in +source+; # defaults to +false+. @@ -143,8 +170,35 @@ require 'json/common' # ruby = JSON.parse(source, {allow_nan: true}) # ruby # => [NaN, Infinity, -Infinity] # +# --- +# +# Option +allow_trailing_comma+ (boolean) specifies whether to allow +# trailing commas in objects and arrays; +# defaults to +false+. +# +# With the default, +false+: +# JSON.parse('[1,]') # unexpected character: ']' at line 1 column 4 (JSON::ParserError) +# +# When enabled: +# JSON.parse('[1,]', allow_trailing_comma: true) # => [1] +# +# --- +# +# Option +allow_control_characters+ (boolean) specifies whether to allow +# unescaped ASCII control characters, such as newlines, in strings; +# defaults to +false+. +# +# With the default, +false+: +# JSON.parse(%{"Hello\nWorld"}) # invalid ASCII control character in string (JSON::ParserError) +# +# When enabled: +# JSON.parse(%{"Hello\nWorld"}, allow_control_characters: true) # => "Hello\nWorld" +# # ====== Output Options # +# Option +freeze+ (boolean) specifies whether the returned objects will be frozen; +# defaults to +false+. +# # Option +symbolize_names+ (boolean) specifies whether returned \Hash keys # should be Symbols; # defaults to +false+ (use Strings). @@ -274,6 +328,25 @@ require 'json/common' # # --- # +# Option +allow_duplicate_key+ (boolean) specifies whether +# hashes with duplicate keys should be allowed or produce an error. +# defaults to emit a deprecation warning. +# +# With the default, (not set): +# Warning[:deprecated] = true +# JSON.generate({ foo: 1, "foo" => 2 }) +# # warning: detected duplicate key "foo" in {foo: 1, "foo" => 2}. +# # This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true` +# # => '{"foo":1,"foo":2}' +# +# With <tt>false</tt> +# JSON.generate({ foo: 1, "foo" => 2 }, allow_duplicate_key: false) +# # detected duplicate key "foo" in {foo: 1, "foo" => 2} (JSON::GeneratorError) +# +# In version 3.0, <tt>false</tt> will become the default. +# +# --- +# # Option +max_nesting+ (\Integer) specifies the maximum nesting depth # in +obj+; defaults to +100+. # @@ -351,6 +424,9 @@ require 'json/common' # # == \JSON Additions # +# Note that JSON Additions must only be used with trusted data, and is +# deprecated. +# # When you "round trip" a non-\String object from Ruby to \JSON and back, # you have a new \String, instead of the object you began with: # ruby0 = Range.new(0, 2) @@ -378,13 +454,13 @@ require 'json/common' # json1 = JSON.generate(ruby) # ruby1 = JSON.parse(json1, create_additions: true) # # Make a nice display. -# display = <<EOT -# Generated JSON: -# Without addition: #{json0} (#{json0.class}) -# With addition: #{json1} (#{json1.class}) -# Parsed JSON: -# Without addition: #{ruby0.inspect} (#{ruby0.class}) -# With addition: #{ruby1.inspect} (#{ruby1.class}) +# display = <<~EOT +# Generated JSON: +# Without addition: #{json0} (#{json0.class}) +# With addition: #{json1} (#{json1.class}) +# Parsed JSON: +# Without addition: #{ruby0.inspect} (#{ruby0.class}) +# With addition: #{ruby1.inspect} (#{ruby1.class}) # EOT # puts display # @@ -562,13 +638,13 @@ require 'json/common' # json1 = JSON.generate(foo1) # obj1 = JSON.parse(json1, create_additions: true) # # Make a nice display. -# display = <<EOT -# Generated JSON: -# Without custom addition: #{json0} (#{json0.class}) -# With custom addition: #{json1} (#{json1.class}) -# Parsed JSON: -# Without custom addition: #{obj0.inspect} (#{obj0.class}) -# With custom addition: #{obj1.inspect} (#{obj1.class}) +# display = <<~EOT +# Generated JSON: +# Without custom addition: #{json0} (#{json0.class}) +# With custom addition: #{json1} (#{json1.class}) +# Parsed JSON: +# Without custom addition: #{obj0.inspect} (#{obj0.class}) +# With custom addition: #{obj1.inspect} (#{obj1.class}) # EOT # puts display # @@ -583,10 +659,5 @@ require 'json/common' # module JSON require 'json/version' - - begin - require 'json/ext' - rescue LoadError - require 'json/pure' - end + require 'json/ext' end diff --git a/ext/json/lib/json/add/bigdecimal.rb b/ext/json/lib/json/add/bigdecimal.rb index b1d0cfa043..dc84572f31 100644 --- a/ext/json/lib/json/add/bigdecimal.rb +++ b/ext/json/lib/json/add/bigdecimal.rb @@ -1,4 +1,4 @@ -#frozen_string_literal: false +# frozen_string_literal: true unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED require 'json' end @@ -35,7 +35,7 @@ class BigDecimal def as_json(*) { JSON.create_id => self.class.name, - 'b' => _dump, + 'b' => _dump.force_encoding(Encoding::UTF_8), } end diff --git a/ext/json/lib/json/add/complex.rb b/ext/json/lib/json/add/complex.rb index ef48c554c6..9e3c6f2d0a 100644 --- a/ext/json/lib/json/add/complex.rb +++ b/ext/json/lib/json/add/complex.rb @@ -1,4 +1,4 @@ -#frozen_string_literal: false +# frozen_string_literal: true unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED require 'json' end diff --git a/ext/json/lib/json/add/core.rb b/ext/json/lib/json/add/core.rb index bfb017c460..61ff454212 100644 --- a/ext/json/lib/json/add/core.rb +++ b/ext/json/lib/json/add/core.rb @@ -1,4 +1,4 @@ -#frozen_string_literal: false +# frozen_string_literal: true # This file requires the implementations of ruby core's custom objects for # serialisation/deserialisation. @@ -7,6 +7,7 @@ require 'json/add/date_time' require 'json/add/exception' require 'json/add/range' require 'json/add/regexp' +require 'json/add/string' require 'json/add/struct' require 'json/add/symbol' require 'json/add/time' diff --git a/ext/json/lib/json/add/date.rb b/ext/json/lib/json/add/date.rb index 19688f751b..88a098b637 100644 --- a/ext/json/lib/json/add/date.rb +++ b/ext/json/lib/json/add/date.rb @@ -1,4 +1,4 @@ -#frozen_string_literal: false +# frozen_string_literal: true unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED require 'json' end diff --git a/ext/json/lib/json/add/date_time.rb b/ext/json/lib/json/add/date_time.rb index d7d42591cf..8b0bb5d181 100644 --- a/ext/json/lib/json/add/date_time.rb +++ b/ext/json/lib/json/add/date_time.rb @@ -1,4 +1,4 @@ -#frozen_string_literal: false +# frozen_string_literal: true unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED require 'json' end diff --git a/ext/json/lib/json/add/exception.rb b/ext/json/lib/json/add/exception.rb index 71d8deb0ea..e85d404982 100644 --- a/ext/json/lib/json/add/exception.rb +++ b/ext/json/lib/json/add/exception.rb @@ -1,4 +1,4 @@ -#frozen_string_literal: false +# frozen_string_literal: true unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED require 'json' end diff --git a/ext/json/lib/json/add/ostruct.rb b/ext/json/lib/json/add/ostruct.rb index 498de17178..7750498144 100644 --- a/ext/json/lib/json/add/ostruct.rb +++ b/ext/json/lib/json/add/ostruct.rb @@ -1,8 +1,11 @@ -#frozen_string_literal: false +# frozen_string_literal: true unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED require 'json' end -require 'ostruct' +begin + require 'ostruct' +rescue LoadError +end class OpenStruct @@ -48,4 +51,4 @@ class OpenStruct def to_json(*args) as_json.to_json(*args) end -end +end if defined?(::OpenStruct) diff --git a/ext/json/lib/json/add/range.rb b/ext/json/lib/json/add/range.rb index 53f54ac372..408d2c32f6 100644 --- a/ext/json/lib/json/add/range.rb +++ b/ext/json/lib/json/add/range.rb @@ -1,4 +1,4 @@ -#frozen_string_literal: false +# frozen_string_literal: true unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED require 'json' end diff --git a/ext/json/lib/json/add/rational.rb b/ext/json/lib/json/add/rational.rb index 8c39a7db55..c95812ea8e 100644 --- a/ext/json/lib/json/add/rational.rb +++ b/ext/json/lib/json/add/rational.rb @@ -1,4 +1,4 @@ -#frozen_string_literal: false +# frozen_string_literal: true unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED require 'json' end diff --git a/ext/json/lib/json/add/regexp.rb b/ext/json/lib/json/add/regexp.rb index b63f49608f..aebfb2db5c 100644 --- a/ext/json/lib/json/add/regexp.rb +++ b/ext/json/lib/json/add/regexp.rb @@ -1,4 +1,4 @@ -#frozen_string_literal: false +# frozen_string_literal: true unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED require 'json' end diff --git a/ext/json/lib/json/add/string.rb b/ext/json/lib/json/add/string.rb new file mode 100644 index 0000000000..9c3bde27fb --- /dev/null +++ b/ext/json/lib/json/add/string.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +class String + # call-seq: json_create(o) + # + # Raw Strings are JSON Objects (the raw bytes are stored in an array for the + # key "raw"). The Ruby String can be created by this class method. + def self.json_create(object) + object["raw"].pack("C*") + end + + # call-seq: to_json_raw_object() + # + # This method creates a raw object hash, that can be nested into + # other data structures and will be generated as a raw string. This + # method should be used, if you want to convert raw strings to JSON + # instead of UTF-8 strings, e. g. binary data. + def to_json_raw_object + { + JSON.create_id => self.class.name, + "raw" => unpack("C*"), + } + end + + # call-seq: to_json_raw(*args) + # + # This method creates a JSON text from the result of a call to + # to_json_raw_object of this String. + def to_json_raw(...) + to_json_raw_object.to_json(...) + end +end diff --git a/ext/json/lib/json/add/struct.rb b/ext/json/lib/json/add/struct.rb index 86847762ac..6760c3d86c 100644 --- a/ext/json/lib/json/add/struct.rb +++ b/ext/json/lib/json/add/struct.rb @@ -1,4 +1,4 @@ -#frozen_string_literal: false +# frozen_string_literal: true unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED require 'json' end diff --git a/ext/json/lib/json/add/symbol.rb b/ext/json/lib/json/add/symbol.rb index b5f3623158..806be4f025 100644 --- a/ext/json/lib/json/add/symbol.rb +++ b/ext/json/lib/json/add/symbol.rb @@ -1,5 +1,4 @@ - -#frozen_string_literal: false +# frozen_string_literal: true unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED require 'json' end @@ -37,8 +36,13 @@ class Symbol # # # {"json_class":"Symbol","s":"foo"} # - def to_json(*a) - as_json.to_json(*a) + def to_json(state = nil, *a) + state = ::JSON::State.from_state(state) + if state.strict? + super + else + as_json.to_json(state, *a) + end end # See #as_json. diff --git a/ext/json/lib/json/add/time.rb b/ext/json/lib/json/add/time.rb index 599ed9e24b..b03d4ff251 100644 --- a/ext/json/lib/json/add/time.rb +++ b/ext/json/lib/json/add/time.rb @@ -1,4 +1,4 @@ -#frozen_string_literal: false +# frozen_string_literal: true unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED require 'json' end @@ -10,11 +10,7 @@ class Time if usec = object.delete('u') # used to be tv_usec -> tv_nsec object['n'] = usec * 1000 end - if method_defined?(:tv_nsec) - at(object['s'], Rational(object['n'], 1000)) - else - at(object['s'], object['n'] / 1000) - end + at(object['s'], Rational(object['n'], 1000)) end # Methods <tt>Time#as_json</tt> and +Time.json_create+ may be used @@ -34,13 +30,10 @@ class Time # # => 2023-11-25 11:00:56.472846644 -0600 # def as_json(*) - nanoseconds = [ tv_usec * 1000 ] - respond_to?(:tv_nsec) and nanoseconds << tv_nsec - nanoseconds = nanoseconds.max { JSON.create_id => self.class.name, 's' => tv_sec, - 'n' => nanoseconds, + 'n' => tv_nsec, } end diff --git a/ext/json/lib/json/common.rb b/ext/json/lib/json/common.rb index 090066012d..877b96814e 100644 --- a/ext/json/lib/json/common.rb +++ b/ext/json/lib/json/common.rb @@ -1,12 +1,123 @@ -#frozen_string_literal: false +# frozen_string_literal: true + require 'json/version' -require 'json/generic_object' module JSON - NOT_SET = Object.new.freeze - private_constant :NOT_SET + autoload :GenericObject, 'json/generic_object' + + module ParserOptions # :nodoc: + class << self + def prepare(opts) + if opts[:object_class] || opts[:array_class] + opts = opts.dup + on_load = opts[:on_load] + + on_load = object_class_proc(opts[:object_class], on_load) if opts[:object_class] + on_load = array_class_proc(opts[:array_class], on_load) if opts[:array_class] + opts[:on_load] = on_load + end + + if opts.fetch(:create_additions, false) != false + opts = create_additions_proc(opts) + end + + opts + end + + private + + def object_class_proc(object_class, on_load) + ->(obj) do + if Hash === obj + object = object_class.new + obj.each { |k, v| object[k] = v } + obj = object + end + on_load.nil? ? obj : on_load.call(obj) + end + end + + def array_class_proc(array_class, on_load) + ->(obj) do + if Array === obj + array = array_class.new + obj.each { |v| array << v } + obj = array + end + on_load.nil? ? obj : on_load.call(obj) + end + end + + # TODO: extract :create_additions support to another gem for version 3.0 + def create_additions_proc(opts) + if opts[:symbolize_names] + raise ArgumentError, "options :symbolize_names and :create_additions cannot be used in conjunction" + end + + opts = opts.dup + create_additions = opts.fetch(:create_additions, false) + on_load = opts[:on_load] + object_class = opts[:object_class] || Hash + + opts[:on_load] = ->(object) do + case object + when String + opts[:match_string]&.each do |pattern, klass| + if match = pattern.match(object) + create_additions_warning if create_additions.nil? + object = klass.json_create(object) + break + end + end + when object_class + if opts[:create_additions] != false + if class_path = object[JSON.create_id] + klass = begin + Object.const_get(class_path) + rescue NameError => e + raise ArgumentError, "can't get const #{class_path}: #{e}" + end + + if klass.respond_to?(:json_creatable?) ? klass.json_creatable? : klass.respond_to?(:json_create) + create_additions_warning if create_additions.nil? + object = klass.json_create(object) + end + end + end + end + + on_load.nil? ? object : on_load.call(object) + end + + opts + end + + def create_additions_warning + JSON.deprecation_warning "JSON.load implicit support for `create_additions: true` is deprecated " \ + "and will be removed in 3.0, use JSON.unsafe_load or explicitly " \ + "pass `create_additions: true`" + end + end + end class << self + def deprecation_warning(message, uplevel = 3) # :nodoc: + gem_root = File.expand_path("..", __dir__) + "/" + caller_locations(uplevel, 10).each do |frame| + if frame.path.nil? || frame.path.start_with?(gem_root) || frame.path.end_with?("/truffle/cext_ruby.rb", ".c") + uplevel += 1 + else + break + end + end + + if RUBY_VERSION >= "3.0" + warn(message, uplevel: uplevel, category: :deprecated) + else + warn(message, uplevel: uplevel) + end + end + # :call-seq: # JSON[object] -> new_array or new_string # @@ -18,17 +129,20 @@ module JSON # Otherwise, calls JSON.generate with +object+ and +opts+ (see method #generate): # ruby = [0, 1, nil] # JSON[ruby] # => '[0,1,null]' - def [](object, opts = {}) - if object.respond_to? :to_str - JSON.parse(object.to_str, opts) - else - JSON.generate(object, opts) + def [](object, opts = nil) + if object.is_a?(String) + return JSON.parse(object, opts) + elsif object.respond_to?(:to_str) + str = object.to_str + if str.is_a?(String) + return JSON.parse(str, opts) + end end + + JSON.generate(object, opts) end - # Returns the JSON parser class that is used by JSON. This is either - # JSON::Ext::Parser or JSON::Pure::Parser: - # JSON.parser # => JSON::Ext::Parser + # Returns the JSON parser class that is used by JSON. attr_reader :parser # Set the JSON parser class _parser_ to be used by JSON. @@ -38,32 +152,13 @@ module JSON const_set :Parser, parser end - # Return the constant located at _path_. The format of _path_ has to be - # either ::A::B::C or A::B::C. In any case, A has to be located at the top - # level (absolute namespace path?). If there doesn't exist a constant at - # the given path, an ArgumentError is raised. - def deep_const_get(path) # :nodoc: - path.to_s.split(/::/).inject(Object) do |p, c| - case - when c.empty? then p - when p.const_defined?(c, true) then p.const_get(c) - else - begin - p.const_missing(c) - rescue NameError => e - raise ArgumentError, "can't get const #{path}: #{e}" - end - end - end - end - # Set the module _generator_ to be used by JSON. def generator=(generator) # :nodoc: old, $VERBOSE = $VERBOSE, nil @generator = generator generator_methods = generator::GeneratorMethods for const in generator_methods.constants - klass = deep_const_get(const) + klass = const_get(const) modul = generator_methods.const_get(const) klass.class_eval do instance_methods(false).each do |m| @@ -73,97 +168,135 @@ module JSON end end self.state = generator::State - const_set :State, self.state - const_set :SAFE_STATE_PROTOTYPE, State.new # for JRuby - const_set :FAST_STATE_PROTOTYPE, create_fast_state - const_set :PRETTY_STATE_PROTOTYPE, create_pretty_state + const_set :State, state ensure $VERBOSE = old end - def create_fast_state - State.new( - :indent => '', - :space => '', - :object_nl => "", - :array_nl => "", - :max_nesting => false - ) - end - - def create_pretty_state - State.new( - :indent => ' ', - :space => ' ', - :object_nl => "\n", - :array_nl => "\n" - ) - end - - # Returns the JSON generator module that is used by JSON. This is - # either JSON::Ext::Generator or JSON::Pure::Generator: - # JSON.generator # => JSON::Ext::Generator + # Returns the JSON generator module that is used by JSON. attr_reader :generator - # Sets or Returns the JSON generator state class that is used by JSON. This is - # either JSON::Ext::Generator::State or JSON::Pure::Generator::State: - # JSON.state # => JSON::Ext::Generator::State + # Sets or Returns the JSON generator state class that is used by JSON. attr_accessor :state - end - DEFAULT_CREATE_ID = 'json_class'.freeze - private_constant :DEFAULT_CREATE_ID + private + + # Called from the extension when a hash has both string and symbol keys + def on_mixed_keys_hash(hash, do_raise) + set = {} + hash.each_key do |key| + key_str = key.to_s + + if set[key_str] + message = "detected duplicate key #{key_str.inspect} in #{hash.inspect}" + if do_raise + raise GeneratorError, message + else + deprecation_warning("#{message}.\nThis will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`") + end + else + set[key_str] = true + end + end + end + + def deprecated_singleton_attr_accessor(*attrs) + args = RUBY_VERSION >= "3.0" ? ", category: :deprecated" : "" + attrs.each do |attr| + singleton_class.class_eval <<~RUBY + def #{attr} + warn "JSON.#{attr} is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args} + @#{attr} + end + + def #{attr}=(val) + warn "JSON.#{attr}= is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args} + @#{attr} = val + end - CREATE_ID_TLS_KEY = "JSON.create_id".freeze - private_constant :CREATE_ID_TLS_KEY + def _#{attr} + @#{attr} + end + RUBY + end + end + end # Sets create identifier, which is used to decide if the _json_create_ # hook of a class should be called; initial value is +json_class+: # JSON.create_id # => 'json_class' def self.create_id=(new_value) - Thread.current[CREATE_ID_TLS_KEY] = new_value.dup.freeze + Thread.current[:"JSON.create_id"] = new_value.dup.freeze end # Returns the current create identifier. # See also JSON.create_id=. def self.create_id - Thread.current[CREATE_ID_TLS_KEY] || DEFAULT_CREATE_ID + Thread.current[:"JSON.create_id"] || 'json_class' end - NaN = 0.0/0 + NaN = Float::NAN - Infinity = 1.0/0 + Infinity = Float::INFINITY MinusInfinity = -Infinity # The base exception for JSON errors. - class JSONError < StandardError - def self.wrap(exception) - obj = new("Wrapped(#{exception.class}): #{exception.message.inspect}") - obj.set_backtrace exception.backtrace - obj - end - end + class JSONError < StandardError; end # This exception is raised if a parser error occurs. - class ParserError < JSONError; end + class ParserError < JSONError + attr_reader :line, :column + end # This exception is raised if the nesting of parsed data structures is too # deep. class NestingError < ParserError; end - # :stopdoc: - class CircularDatastructure < NestingError; end - # :startdoc: - # This exception is raised if a generator or unparser error occurs. - class GeneratorError < JSONError; end - # For backwards compatibility - UnparserError = GeneratorError # :nodoc: + class GeneratorError < JSONError + attr_reader :invalid_object - # This exception is raised if the required unicode support is missing on the - # system. Usually this means that the iconv library is not installed. - class MissingUnicodeSupport < JSONError; end + def initialize(message, invalid_object = nil) + super(message) + @invalid_object = invalid_object + end + + def detailed_message(...) + # Exception#detailed_message doesn't exist until Ruby 3.2 + super_message = defined?(super) ? super : message + + if @invalid_object.nil? + super_message + else + "#{super_message}\nInvalid object: #{@invalid_object.inspect}" + end + end + end + + # Fragment of JSON document that is to be included as is: + # fragment = JSON::Fragment.new("[1, 2, 3]") + # JSON.generate({ count: 3, items: fragments }) + # + # This allows to easily assemble multiple JSON fragments that have + # been persisted somewhere without having to parse them nor resorting + # to string interpolation. + # + # Note: no validation is performed on the provided string. It is the + # responsibility of the caller to ensure the string contains valid JSON. + Fragment = Struct.new(:json) do + def initialize(json) + unless string = String.try_convert(json) + raise TypeError, " no implicit conversion of #{json.class} into String" + end + + super(string) + end + + def to_json(state = nil, *) + json + end + end module_function @@ -195,17 +328,17 @@ module JSON # {Parsing \JSON}[#module-JSON-label-Parsing+JSON]. # # Parses nested JSON objects: - # source = <<-EOT - # { - # "name": "Dave", - # "age" :40, - # "hats": [ - # "Cattleman's", - # "Panama", - # "Tophat" - # ] - # } - # EOT + # source = <<~JSON + # { + # "name": "Dave", + # "age" :40, + # "hats": [ + # "Cattleman's", + # "Panama", + # "Tophat" + # ] + # } + # JSON # ruby = JSON.parse(source) # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]} # @@ -215,10 +348,17 @@ module JSON # # Raises JSON::ParserError (783: unexpected token at ''): # JSON.parse('') # - def parse(source, opts = {}) - Parser.new(source, **(opts||{})).parse + def parse(source, opts = nil) + opts = ParserOptions.prepare(opts) unless opts.nil? + Parser.parse(source, opts) end + PARSE_L_OPTIONS = { + max_nesting: false, + allow_nan: true, + }.freeze + private_constant :PARSE_L_OPTIONS + # :call-seq: # JSON.parse!(source, opts) -> object # @@ -230,12 +370,12 @@ module JSON # - Option +max_nesting+, if not provided, defaults to +false+, # which disables checking for nesting depth. # - Option +allow_nan+, if not provided, defaults to +true+. - def parse!(source, opts = {}) - opts = { - :max_nesting => false, - :allow_nan => true - }.merge(opts) - Parser.new(source, **(opts||{})).parse + def parse!(source, opts = nil) + if opts.nil? + parse(source, PARSE_L_OPTIONS) + else + parse(source, PARSE_L_OPTIONS.merge(opts)) + end end # :call-seq: @@ -245,8 +385,8 @@ module JSON # parse(File.read(path), opts) # # See method #parse. - def load_file(filespec, opts = {}) - parse(File.read(filespec), opts) + def load_file(filespec, opts = nil) + parse(File.read(filespec, encoding: Encoding::UTF_8), opts) end # :call-seq: @@ -256,8 +396,8 @@ module JSON # JSON.parse!(File.read(path, opts)) # # See method #parse! - def load_file!(filespec, opts = {}) - parse!(File.read(filespec), opts) + def load_file!(filespec, opts = nil) + parse!(File.read(filespec, encoding: Encoding::UTF_8), opts) end # :call-seq: @@ -265,7 +405,7 @@ module JSON # # Returns a \String containing the generated \JSON data. # - # See also JSON.fast_generate, JSON.pretty_generate. + # See also JSON.pretty_generate. # # Argument +obj+ is the Ruby object to be converted to \JSON. # @@ -298,20 +438,12 @@ module JSON # def generate(obj, opts = nil) if State === opts - state = opts + opts.generate(obj) else - state = State.new(opts) + State.generate(obj, opts, nil) end - state.generate(obj) end - # :stopdoc: - # I want to deprecate these later, so I'll first be silent about them, and - # later delete them. - alias unparse generate - module_function :unparse - # :startdoc: - # :call-seq: # JSON.fast_generate(obj, opts) -> new_string # @@ -326,19 +458,21 @@ module JSON # # Raises SystemStackError (stack level too deep): # JSON.fast_generate(a) def fast_generate(obj, opts = nil) - if State === opts - state = opts + if RUBY_VERSION >= "3.0" + warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated else - state = JSON.create_fast_state.configure(opts) + warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1 end - state.generate(obj) + generate(obj, opts) end - # :stopdoc: - # I want to deprecate these later, so I'll first be silent about them, and later delete them. - alias fast_unparse fast_generate - module_function :fast_unparse - # :startdoc: + PRETTY_GENERATE_OPTIONS = { + indent: ' ', + space: ' ', + object_nl: "\n", + array_nl: "\n", + }.freeze + private_constant :PRETTY_GENERATE_OPTIONS # :call-seq: # JSON.pretty_generate(obj, opts = nil) -> new_string @@ -371,49 +505,60 @@ module JSON # } # def pretty_generate(obj, opts = nil) - if State === opts - state, opts = opts, nil - else - state = JSON.create_pretty_state - end + return opts.generate(obj) if State === opts + + options = PRETTY_GENERATE_OPTIONS + if opts - if opts.respond_to? :to_hash - opts = opts.to_hash - elsif opts.respond_to? :to_h - opts = opts.to_h - else - raise TypeError, "can't convert #{opts.class} into Hash" + unless opts.is_a?(Hash) + if opts.respond_to? :to_hash + opts = opts.to_hash + elsif opts.respond_to? :to_h + opts = opts.to_h + else + raise TypeError, "can't convert #{opts.class} into Hash" + end end - state.configure(opts) + options = options.merge(opts) end - state.generate(obj) + + State.generate(obj, options, nil) end - # :stopdoc: - # I want to deprecate these later, so I'll first be silent about them, and later delete them. - alias pretty_unparse pretty_generate - module_function :pretty_unparse - # :startdoc: + # Sets or returns default options for the JSON.unsafe_load method. + # Initially: + # opts = JSON.load_default_options + # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true} + deprecated_singleton_attr_accessor :unsafe_load_default_options - class << self - # Sets or returns default options for the JSON.load method. - # Initially: - # opts = JSON.load_default_options - # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true} - attr_accessor :load_default_options - end - self.load_default_options = { + @unsafe_load_default_options = { :max_nesting => false, :allow_nan => true, - :allow_blank => true, + :allow_blank => true, :create_additions => true, } + # Sets or returns default options for the JSON.load method. + # Initially: + # opts = JSON.load_default_options + # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true} + deprecated_singleton_attr_accessor :load_default_options + + @load_default_options = { + :allow_nan => true, + :allow_blank => true, + :create_additions => nil, + } # :call-seq: - # JSON.load(source, proc = nil, options = {}) -> object + # JSON.unsafe_load(source, options = {}) -> object + # JSON.unsafe_load(source, proc = nil, options = {}) -> object # # Returns the Ruby objects created by parsing the given +source+. # + # BEWARE: This method is meant to serialise data from trusted user input, + # like from your own database server or clients under your control, it could + # be dangerous to allow untrusted users to pass JSON sources into it. + # # - Argument +source+ must be, or be convertible to, a \String: # - If +source+ responds to instance method +to_str+, # <tt>source.to_str</tt> becomes the source. @@ -428,12 +573,9 @@ module JSON # - Argument +proc+, if given, must be a \Proc that accepts one argument. # It will be called recursively with each result (depth-first order). # See details below. - # BEWARE: This method is meant to serialise data from trusted user input, - # like from your own database server or clients under your control, it could - # be dangerous to allow untrusted users to pass JSON sources into it. # - Argument +opts+, if given, contains a \Hash of options for the parsing. # See {Parsing Options}[#module-JSON-label-Parsing+Options]. - # The default options can be changed via method JSON.load_default_options=. + # The default options can be changed via method JSON.unsafe_load_default_options=. # # --- # @@ -441,17 +583,188 @@ module JSON # <tt>parse(source, opts)</tt>; see #parse. # # Source for following examples: - # source = <<-EOT + # source = <<~JSON + # { + # "name": "Dave", + # "age" :40, + # "hats": [ + # "Cattleman's", + # "Panama", + # "Tophat" + # ] + # } + # JSON + # + # Load a \String: + # ruby = JSON.unsafe_load(source) + # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]} + # + # Load an \IO object: + # require 'stringio' + # object = JSON.unsafe_load(StringIO.new(source)) + # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]} + # + # Load a \File object: + # path = 't.json' + # File.write(path, source) + # File.open(path) do |file| + # JSON.unsafe_load(file) + # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]} + # + # --- + # + # When +proc+ is given: + # - Modifies +source+ as above. + # - Gets the +result+ from calling <tt>parse(source, opts)</tt>. + # - Recursively calls <tt>proc(result)</tt>. + # - Returns the final result. + # + # Example: + # require 'json' + # + # # Some classes for the example. + # class Base + # def initialize(attributes) + # @attributes = attributes + # end + # end + # class User < Base; end + # class Account < Base; end + # class Admin < Base; end + # # The JSON source. + # json = <<-EOF # { - # "name": "Dave", - # "age" :40, - # "hats": [ - # "Cattleman's", - # "Panama", - # "Tophat" - # ] + # "users": [ + # {"type": "User", "username": "jane", "email": "jane@example.com"}, + # {"type": "User", "username": "john", "email": "john@example.com"} + # ], + # "accounts": [ + # {"account": {"type": "Account", "paid": true, "account_id": "1234"}}, + # {"account": {"type": "Account", "paid": false, "account_id": "1235"}} + # ], + # "admins": {"type": "Admin", "password": "0wn3d"} # } - # EOT + # EOF + # # Deserializer method. + # def deserialize_obj(obj, safe_types = %w(User Account Admin)) + # type = obj.is_a?(Hash) && obj["type"] + # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj + # end + # # Call to JSON.unsafe_load + # ruby = JSON.unsafe_load(json, proc {|obj| + # case obj + # when Hash + # obj.each {|k, v| obj[k] = deserialize_obj v } + # when Array + # obj.map! {|v| deserialize_obj v } + # end + # obj + # }) + # pp ruby + # Output: + # {"users"=> + # [#<User:0x00000000064c4c98 + # @attributes= + # {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>, + # #<User:0x00000000064c4bd0 + # @attributes= + # {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>], + # "accounts"=> + # [{"account"=> + # #<Account:0x00000000064c4928 + # @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>}, + # {"account"=> + # #<Account:0x00000000064c4680 + # @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}], + # "admins"=> + # #<Admin:0x00000000064c41f8 + # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>} + # + def unsafe_load(source, proc = nil, options = nil) + opts = if options.nil? + if proc && proc.is_a?(Hash) + options, proc = proc, nil + options + else + _unsafe_load_default_options + end + else + _unsafe_load_default_options.merge(options) + end + + unless source.is_a?(String) + if source.respond_to? :to_str + source = source.to_str + elsif source.respond_to? :to_io + source = source.to_io.read + elsif source.respond_to?(:read) + source = source.read + end + end + + if opts[:allow_blank] && (source.nil? || source.empty?) + source = 'null' + end + + if proc + opts = opts.dup + opts[:on_load] = proc.to_proc + end + + parse(source, opts) + end + + # :call-seq: + # JSON.load(source, options = {}) -> object + # JSON.load(source, proc = nil, options = {}) -> object + # + # Returns the Ruby objects created by parsing the given +source+. + # + # BEWARE: This method is meant to serialise data from trusted user input, + # like from your own database server or clients under your control, it could + # be dangerous to allow untrusted users to pass JSON sources into it. + # If you must use it, use JSON.unsafe_load instead to make it clear. + # + # Since JSON version 2.8.0, `load` emits a deprecation warning when a + # non native type is deserialized, without `create_additions` being explicitly + # enabled, and in JSON version 3.0, `load` will have `create_additions` disabled + # by default. + # + # - Argument +source+ must be, or be convertible to, a \String: + # - If +source+ responds to instance method +to_str+, + # <tt>source.to_str</tt> becomes the source. + # - If +source+ responds to instance method +to_io+, + # <tt>source.to_io.read</tt> becomes the source. + # - If +source+ responds to instance method +read+, + # <tt>source.read</tt> becomes the source. + # - If both of the following are true, source becomes the \String <tt>'null'</tt>: + # - Option +allow_blank+ specifies a truthy value. + # - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>. + # - Otherwise, +source+ remains the source. + # - Argument +proc+, if given, must be a \Proc that accepts one argument. + # It will be called recursively with each result (depth-first order). + # See details below. + # - Argument +opts+, if given, contains a \Hash of options for the parsing. + # See {Parsing Options}[#module-JSON-label-Parsing+Options]. + # The default options can be changed via method JSON.load_default_options=. + # + # --- + # + # When no +proc+ is given, modifies +source+ as above and returns the result of + # <tt>parse(source, opts)</tt>; see #parse. + # + # Source for following examples: + # source = <<~JSON + # { + # "name": "Dave", + # "age" :40, + # "hats": [ + # "Cattleman's", + # "Panama", + # "Tophat" + # ] + # } + # JSON # # Load a \String: # ruby = JSON.load(source) @@ -516,6 +829,7 @@ module JSON # when Array # obj.map! {|v| deserialize_obj v } # end + # obj # }) # pp ruby # Output: @@ -537,51 +851,53 @@ module JSON # #<Admin:0x00000000064c41f8 # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>} # - def load(source, proc = nil, options = {}) - opts = load_default_options.merge options - if source.respond_to? :to_str - source = source.to_str - elsif source.respond_to? :to_io - source = source.to_io.read - elsif source.respond_to?(:read) - source = source.read + def load(source, proc = nil, options = nil) + if proc && options.nil? && proc.is_a?(Hash) + options = proc + proc = nil end + + opts = if options.nil? + if proc && proc.is_a?(Hash) + options, proc = proc, nil + options + else + _load_default_options + end + else + _load_default_options.merge(options) + end + + unless source.is_a?(String) + if source.respond_to? :to_str + source = source.to_str + elsif source.respond_to? :to_io + source = source.to_io.read + elsif source.respond_to?(:read) + source = source.read + end + end + if opts[:allow_blank] && (source.nil? || source.empty?) source = 'null' end - result = parse(source, opts) - recurse_proc(result, &proc) if proc - result - end - # Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_ - def recurse_proc(result, &proc) # :nodoc: - case result - when Array - result.each { |x| recurse_proc x, &proc } - proc.call result - when Hash - result.each { |x, y| recurse_proc x, &proc; recurse_proc y, &proc } - proc.call result - else - proc.call result + if proc + opts = opts.dup + opts[:on_load] = proc.to_proc end - end - - alias restore load - module_function :restore - class << self - # Sets or returns the default options for the JSON.dump method. - # Initially: - # opts = JSON.dump_default_options - # opts # => {:max_nesting=>false, :allow_nan=>true, :script_safe=>false} - attr_accessor :dump_default_options + parse(source, opts) end - self.dump_default_options = { + + # Sets or returns the default options for the JSON.dump method. + # Initially: + # opts = JSON.dump_default_options + # opts # => {:max_nesting=>false, :allow_nan=>true} + deprecated_singleton_attr_accessor :dump_default_options + @dump_default_options = { :max_nesting => false, :allow_nan => true, - :script_safe => false, } # :call-seq: @@ -612,40 +928,172 @@ module JSON # Output: # {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"} def dump(obj, anIO = nil, limit = nil, kwargs = nil) - io_limit_opt = [anIO, limit, kwargs].compact - kwargs = io_limit_opt.pop if io_limit_opt.last.is_a?(Hash) - anIO, limit = io_limit_opt - if anIO.respond_to?(:to_io) - anIO = anIO.to_io - elsif limit.nil? && !anIO.respond_to?(:write) - anIO, limit = nil, anIO - end - opts = JSON.dump_default_options + if kwargs.nil? + if limit.nil? + if anIO.is_a?(Hash) + kwargs = anIO + anIO = nil + end + elsif limit.is_a?(Hash) + kwargs = limit + limit = nil + end + end + + unless anIO.nil? + if anIO.respond_to?(:to_io) + anIO = anIO.to_io + elsif limit.nil? && !anIO.respond_to?(:write) + anIO, limit = nil, anIO + end + end + + opts = JSON._dump_default_options opts = opts.merge(:max_nesting => limit) if limit - opts = merge_dump_options(opts, **kwargs) if kwargs - result = generate(obj, opts) - if anIO - anIO.write result - anIO + opts = opts.merge(kwargs) if kwargs + + begin + State.generate(obj, opts, anIO) + rescue JSON::NestingError + raise ArgumentError, "exceed depth limit" + end + end + + # :stopdoc: + # All these were meant to be deprecated circa 2009, but were just set as undocumented + # so usage still exist in the wild. + def unparse(...) + if RUBY_VERSION >= "3.0" + warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated else - result + warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1 end - rescue JSON::NestingError - raise ArgumentError, "exceed depth limit" + generate(...) end + module_function :unparse - # Encodes string using String.encode. - def self.iconv(to, from, string) - string.encode(to, from) + def fast_unparse(...) + if RUBY_VERSION >= "3.0" + warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated + else + warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1 + end + generate(...) end + module_function :fast_unparse - def merge_dump_options(opts, strict: NOT_SET) - opts = opts.merge(strict: strict) if NOT_SET != strict - opts + def pretty_unparse(...) + if RUBY_VERSION >= "3.0" + warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1, category: :deprecated + else + warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1 + end + pretty_generate(...) + end + module_function :fast_unparse + + def restore(...) + if RUBY_VERSION >= "3.0" + warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1, category: :deprecated + else + warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1 + end + load(...) end + module_function :restore class << self - private :merge_dump_options + private + + def const_missing(const_name) + case const_name + when :PRETTY_STATE_PROTOTYPE + if RUBY_VERSION >= "3.0" + warn "JSON::PRETTY_STATE_PROTOTYPE is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1, category: :deprecated + else + warn "JSON::PRETTY_STATE_PROTOTYPE is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1 + end + state.new(PRETTY_GENERATE_OPTIONS) + else + super + end + end + end + # :startdoc: + + # JSON::Coder holds a parser and generator configuration. + # + # module MyApp + # JSONC_CODER = JSON::Coder.new( + # allow_trailing_comma: true + # ) + # end + # + # MyApp::JSONC_CODER.load(document) + # + class Coder + # :call-seq: + # JSON.new(options = nil, &block) + # + # Argument +options+, if given, contains a \Hash of options for both parsing and generating. + # See {Parsing Options}[#module-JSON-label-Parsing+Options], and {Generating Options}[#module-JSON-label-Generating+Options]. + # + # For generation, the <tt>strict: true</tt> option is always set. When a Ruby object with no native \JSON counterpart is + # encountered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native + # \JSON counterpart: + # + # module MyApp + # API_JSON_CODER = JSON::Coder.new do |object| + # case object + # when Time + # object.iso8601(3) + # else + # object # Unknown type, will raise + # end + # end + # end + # + # puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z" + # + def initialize(options = nil, &as_json) + if options.nil? + options = { strict: true } + else + options = options.dup + options[:strict] = true + end + options[:as_json] = as_json if as_json + + @state = State.new(options).freeze + @parser_config = Ext::Parser::Config.new(ParserOptions.prepare(options)).freeze + end + + # call-seq: + # dump(object) -> String + # dump(object, io) -> io + # + # Serialize the given object into a \JSON document. + def dump(object, io = nil) + @state.generate(object, io) + end + alias_method :generate, :dump + + # call-seq: + # load(string) -> Object + # + # Parse the given \JSON document and return an equivalent Ruby object. + def load(source) + @parser_config.parse(source) + end + alias_method :parse, :load + + # call-seq: + # load(path) -> Object + # + # Parse the given \JSON document and return an equivalent Ruby object. + def load_file(path) + load(File.read(path, encoding: Encoding::UTF_8)) + end end end @@ -655,8 +1103,14 @@ module ::Kernel # Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in # one line. def j(*objs) + if RUBY_VERSION >= "3.0" + warn "Kernel#j is deprecated and will be removed in json 3.0.0", uplevel: 1, category: :deprecated + else + warn "Kernel#j is deprecated and will be removed in json 3.0.0", uplevel: 1 + end + objs.each do |obj| - puts JSON::generate(obj, :allow_nan => true, :max_nesting => false) + puts JSON.generate(obj, :allow_nan => true, :max_nesting => false) end nil end @@ -664,8 +1118,14 @@ module ::Kernel # Outputs _objs_ to STDOUT as JSON strings in a pretty format, with # indentation and over many lines. def jj(*objs) + if RUBY_VERSION >= "3.0" + warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1, category: :deprecated + else + warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1 + end + objs.each do |obj| - puts JSON::pretty_generate(obj, :allow_nan => true, :max_nesting => false) + puts JSON.pretty_generate(obj, :allow_nan => true, :max_nesting => false) end nil end @@ -676,22 +1136,7 @@ module ::Kernel # # The _opts_ argument is passed through to generate/parse respectively. See # generate and parse for their documentation. - def JSON(object, *args) - if object.respond_to? :to_str - JSON.parse(object.to_str, args.first) - else - JSON.generate(object, args.first) - end - end -end - -# Extends any Class to include _json_creatable?_ method. -class ::Class - # Returns true if this class can be used to create an instance - # from a serialised JSON string. The class has to implement a class - # method _json_create_ that expects a hash as first parameter. The hash - # should include the required data. - def json_creatable? - respond_to?(:json_create) + def JSON(object, opts = nil) + JSON[object, opts] end end diff --git a/ext/json/lib/json/ext.rb b/ext/json/lib/json/ext.rb index 7264a857fa..5bacc5e371 100644 --- a/ext/json/lib/json/ext.rb +++ b/ext/json/lib/json/ext.rb @@ -1,15 +1,45 @@ +# frozen_string_literal: true + require 'json/common' module JSON # This module holds all the modules/classes that implement JSON's # functionality as C extensions. module Ext + class Parser + class << self + def parse(...) + new(...).parse + end + alias_method :parse, :parse # Allow redefinition by extensions + end + + def initialize(source, opts = nil) + @source = source + @config = Config.new(opts) + end + + def source + @source.dup + end + + def parse + @config.parse(@source) + end + end + require 'json/ext/parser' - require 'json/ext/generator' - $DEBUG and warn "Using Ext extension for JSON." - JSON.parser = Parser - JSON.generator = Generator + Ext::Parser::Config = Ext::ParserConfig + JSON.parser = Ext::Parser + + if RUBY_ENGINE == 'truffleruby' + require 'json/truffle_ruby/generator' + JSON.generator = JSON::TruffleRuby::Generator + else + require 'json/ext/generator' + JSON.generator = Generator + end end - JSON_LOADED = true unless defined?(::JSON::JSON_LOADED) + JSON_LOADED = true unless defined?(JSON::JSON_LOADED) end diff --git a/ext/json/lib/json/ext/generator/state.rb b/ext/json/lib/json/ext/generator/state.rb new file mode 100644 index 0000000000..ce5c185cab --- /dev/null +++ b/ext/json/lib/json/ext/generator/state.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +module JSON + module Ext + module Generator + class State + # call-seq: new(opts = {}) + # + # Instantiates a new State object, configured by _opts_. + # + # Argument +opts+, if given, contains a \Hash of options for the generation. + # See {Generating Options}[#module-JSON-label-Generating+Options]. + def initialize(opts = nil) + if opts && !opts.empty? + configure(opts) + end + end + + # call-seq: configure(opts) + # + # Configure this State instance with the Hash _opts_, and return + # itself. + def configure(opts) + unless opts.is_a?(Hash) + if opts.respond_to?(:to_hash) + opts = opts.to_hash + elsif opts.respond_to?(:to_h) + opts = opts.to_h + else + raise TypeError, "can't convert #{opts.class} into Hash" + end + end + _configure(opts) + end + + alias_method :merge, :configure + + # call-seq: to_h + # + # Returns the configuration instance variables as a hash, that can be + # passed to the configure method. + def to_h + result = { + indent: indent, + space: space, + space_before: space_before, + object_nl: object_nl, + array_nl: array_nl, + as_json: as_json, + allow_nan: allow_nan?, + ascii_only: ascii_only?, + max_nesting: max_nesting, + script_safe: script_safe?, + strict: strict?, + depth: depth, + buffer_initial_length: buffer_initial_length, + } + + allow_duplicate_key = allow_duplicate_key? + unless allow_duplicate_key.nil? + result[:allow_duplicate_key] = allow_duplicate_key + end + + instance_variables.each do |iv| + iv = iv.to_s[1..-1] + result[iv.to_sym] = self[iv] + end + + result + end + + alias_method :to_hash, :to_h + + # call-seq: [](name) + # + # Returns the value returned by method +name+. + def [](name) + ::JSON.deprecation_warning("JSON::State#[] is deprecated and will be removed in json 3.0.0") + + if respond_to?(name) + __send__(name) + else + instance_variable_get("@#{name}") if + instance_variables.include?("@#{name}".to_sym) # avoid warning + end + end + + # call-seq: []=(name, value) + # + # Sets the attribute name to value. + def []=(name, value) + ::JSON.deprecation_warning("JSON::State#[]= is deprecated and will be removed in json 3.0.0") + + if respond_to?(name_writer = "#{name}=") + __send__ name_writer, value + else + instance_variable_set "@#{name}", value + end + end + end + end + end +end diff --git a/ext/json/lib/json/generic_object.rb b/ext/json/lib/json/generic_object.rb index 108309db26..5c8ace354b 100644 --- a/ext/json/lib/json/generic_object.rb +++ b/ext/json/lib/json/generic_object.rb @@ -1,5 +1,9 @@ -#frozen_string_literal: false -require 'ostruct' +# frozen_string_literal: true +begin + require 'ostruct' +rescue LoadError + warn "JSON::GenericObject requires 'ostruct'. Please install it with `gem install ostruct`." +end module JSON class GenericObject < OpenStruct @@ -48,14 +52,6 @@ module JSON table end - def [](name) - __send__(name) - end unless method_defined?(:[]) - - def []=(name, value) - __send__("#{name}=", value) - end unless method_defined?(:[]=) - def |(other) self.class[other.to_hash.merge(to_hash)] end @@ -67,5 +63,5 @@ module JSON def to_json(*a) as_json.to_json(*a) end - end + end if defined?(::OpenStruct) end diff --git a/ext/json/lib/json/version.rb b/ext/json/lib/json/version.rb index b43ceecdcd..631beba83e 100644 --- a/ext/json/lib/json/version.rb +++ b/ext/json/lib/json/version.rb @@ -1,9 +1,5 @@ -# frozen_string_literal: false +# frozen_string_literal: true + module JSON - # JSON version - VERSION = '2.7.1' - VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: - VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: - VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: - VERSION_BUILD = VERSION_ARRAY[2] # :nodoc: + VERSION = '2.18.0' end diff --git a/ext/json/parser/depend b/ext/json/parser/depend index cb6e547d29..d4737b1dfb 100644 --- a/ext/json/parser/depend +++ b/ext/json/parser/depend @@ -1,5 +1,5 @@ $(OBJS): $(ruby_headers) -parser.o: parser.c parser.h $(srcdir)/../fbuffer/fbuffer.h +parser.o: parser.c $(srcdir)/../fbuffer/fbuffer.h # AUTOGENERATED DEPENDENCIES START parser.o: $(RUBY_EXTCONF_H) @@ -141,6 +141,7 @@ parser.o: $(hdrdir)/ruby/internal/intern/re.h parser.o: $(hdrdir)/ruby/internal/intern/ruby.h parser.o: $(hdrdir)/ruby/internal/intern/select.h parser.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +parser.o: $(hdrdir)/ruby/internal/intern/set.h parser.o: $(hdrdir)/ruby/internal/intern/signal.h parser.o: $(hdrdir)/ruby/internal/intern/sprintf.h parser.o: $(hdrdir)/ruby/internal/intern/string.h @@ -160,6 +161,7 @@ parser.o: $(hdrdir)/ruby/internal/special_consts.h parser.o: $(hdrdir)/ruby/internal/static_assert.h parser.o: $(hdrdir)/ruby/internal/stdalign.h parser.o: $(hdrdir)/ruby/internal/stdbool.h +parser.o: $(hdrdir)/ruby/internal/stdckdint.h parser.o: $(hdrdir)/ruby/internal/symbol.h parser.o: $(hdrdir)/ruby/internal/value.h parser.o: $(hdrdir)/ruby/internal/value_type.h @@ -173,7 +175,8 @@ parser.o: $(hdrdir)/ruby/ruby.h parser.o: $(hdrdir)/ruby/st.h parser.o: $(hdrdir)/ruby/subst.h parser.o: $(srcdir)/../fbuffer/fbuffer.h +parser.o: $(srcdir)/../json.h +parser.o: $(srcdir)/../simd/simd.h +parser.o: $(srcdir)/../vendor/ryu.h parser.o: parser.c -parser.o: parser.h -parser.o: parser.rl # AUTOGENERATED DEPENDENCIES END diff --git a/ext/json/parser/extconf.rb b/ext/json/parser/extconf.rb index feb586e1b4..2440e66d8b 100644 --- a/ext/json/parser/extconf.rb +++ b/ext/json/parser/extconf.rb @@ -1,32 +1,16 @@ -# frozen_string_literal: false +# frozen_string_literal: true require 'mkmf' -have_func("rb_enc_raise", "ruby.h") -have_func("rb_enc_interned_str", "ruby.h") +$defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0" +have_func("rb_enc_interned_str", "ruby/encoding.h") # RUBY_VERSION >= 3.0 +have_func("rb_str_to_interned_str", "ruby.h") # RUBY_VERSION >= 3.0 +have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2 +have_func("rb_hash_bulk_insert", "ruby.h") # Missing on TruffleRuby -# checking if String#-@ (str_uminus) dedupes... ' -begin - a = -(%w(t e s t).join) - b = -(%w(t e s t).join) - if a.equal?(b) - $CFLAGS << ' -DSTR_UMINUS_DEDUPE=1 ' - else - $CFLAGS << ' -DSTR_UMINUS_DEDUPE=0 ' - end -rescue NoMethodError - $CFLAGS << ' -DSTR_UMINUS_DEDUPE=0 ' -end +append_cflags("-std=c99") -# checking if String#-@ (str_uminus) directly interns frozen strings... ' -begin - s = rand.to_s.freeze - if (-s).equal?(s) && (-s.dup).equal?(s) - $CFLAGS << ' -DSTR_UMINUS_DEDUPE_FROZEN=1 ' - else - $CFLAGS << ' -DSTR_UMINUS_DEDUPE_FROZEN=0 ' - end -rescue NoMethodError - $CFLAGS << ' -DSTR_UMINUS_DEDUPE_FROZEN=0 ' +if enable_config('parser-use-simd', default=!ENV["JSON_DISABLE_SIMD"]) + load __dir__ + "/../simd/conf.rb" end create_makefile 'json/ext/parser' diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index 57f87432b6..f1ea1b6abb 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -1,69 +1,301 @@ -/* This file is automatically generated from parser.rl by using ragel */ -#line 1 "parser.rl" -#include "../fbuffer/fbuffer.h" -#include "parser.h" - -#if defined HAVE_RUBY_ENCODING_H -# define EXC_ENCODING rb_utf8_encoding(), -# ifndef HAVE_RB_ENC_RAISE +#include "../json.h" +#include "../vendor/ryu.h" +#include "../simd/simd.h" + +static VALUE mJSON, eNestingError, Encoding_UTF_8; +static VALUE CNaN, CInfinity, CMinusInfinity; + +static ID i_new, i_try_convert, i_uminus, i_encode; + +static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_allow_control_characters, sym_symbolize_names, sym_freeze, + sym_decimal_class, sym_on_load, sym_allow_duplicate_key; + +static int binary_encindex; +static int utf8_encindex; + +#ifndef HAVE_RB_HASH_BULK_INSERT +// For TruffleRuby static void -enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...) +rb_hash_bulk_insert(long count, const VALUE *pairs, VALUE hash) { - va_list args; - VALUE mesg; + long index = 0; + while (index < count) { + VALUE name = pairs[index++]; + VALUE value = pairs[index++]; + rb_hash_aset(hash, name, value); + } + RB_GC_GUARD(hash); +} +#endif - va_start(args, fmt); - mesg = rb_enc_vsprintf(enc, fmt, args); - va_end(args); +#ifndef HAVE_RB_HASH_NEW_CAPA +#define rb_hash_new_capa(n) rb_hash_new() +#endif - rb_exc_raise(rb_exc_new3(exc, mesg)); +#ifndef HAVE_RB_STR_TO_INTERNED_STR +static VALUE rb_str_to_interned_str(VALUE str) +{ + return rb_funcall(rb_str_freeze(str), i_uminus, 0); } -# define rb_enc_raise enc_raise +#endif + +/* name cache */ + +#include <string.h> +#include <ctype.h> + +// Object names are likely to be repeated, and are frozen. +// As such we can re-use them if we keep a cache of the ones we've seen so far, +// and save much more expensive lookups into the global fstring table. +// This cache implementation is deliberately simple, as we're optimizing for compactness, +// to be able to fit safely on the stack. +// As such, binary search into a sorted array gives a good tradeoff between compactness and +// performance. +#define JSON_RVALUE_CACHE_CAPA 63 +typedef struct rvalue_cache_struct { + int length; + VALUE entries[JSON_RVALUE_CACHE_CAPA]; +} rvalue_cache; + +static rb_encoding *enc_utf8; + +#define JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH 55 + +static inline VALUE build_interned_string(const char *str, const long length) +{ +# ifdef HAVE_RB_ENC_INTERNED_STR + return rb_enc_interned_str(str, length, enc_utf8); +# else + VALUE rstring = rb_utf8_str_new(str, length); + return rb_funcall(rb_str_freeze(rstring), i_uminus, 0); # endif -#else -# define EXC_ENCODING /* nothing */ -# define rb_enc_raise rb_raise +} + +static inline VALUE build_symbol(const char *str, const long length) +{ + return rb_str_intern(build_interned_string(str, length)); +} + +static void rvalue_cache_insert_at(rvalue_cache *cache, int index, VALUE rstring) +{ + MEMMOVE(&cache->entries[index + 1], &cache->entries[index], VALUE, cache->length - index); + cache->length++; + cache->entries[index] = rstring; +} + +#define rstring_cache_memcmp memcmp + +#if JSON_CPU_LITTLE_ENDIAN_64BITS +#if __has_builtin(__builtin_bswap64) +#undef rstring_cache_memcmp +ALWAYS_INLINE(static) int rstring_cache_memcmp(const char *str, const char *rptr, const long length) +{ + // The libc memcmp has numerous complex optimizations, but in this particular case, + // we know the string is small (JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH), so being able to + // inline a simpler memcmp outperforms calling the libc version. + long i = 0; + + for (; i + 8 <= length; i += 8) { + uint64_t a, b; + memcpy(&a, str + i, 8); + memcpy(&b, rptr + i, 8); + if (a != b) { + a = __builtin_bswap64(a); + b = __builtin_bswap64(b); + return (a < b) ? -1 : 1; + } + } + + for (; i < length; i++) { + if (str[i] != rptr[i]) { + return (str[i] < rptr[i]) ? -1 : 1; + } + } + + return 0; +} +#endif #endif -/* unicode */ +ALWAYS_INLINE(static) int rstring_cache_cmp(const char *str, const long length, VALUE rstring) +{ + const char *rstring_ptr; + long rstring_length; -static const signed char digit_values[256] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, - -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1 + RSTRING_GETMEM(rstring, rstring_ptr, rstring_length); + + if (length == rstring_length) { + return rstring_cache_memcmp(str, rstring_ptr, length); + } else { + return (int)(length - rstring_length); + } +} + +ALWAYS_INLINE(static) VALUE rstring_cache_fetch(rvalue_cache *cache, const char *str, const long length) +{ + int low = 0; + int high = cache->length - 1; + + while (low <= high) { + int mid = (high + low) >> 1; + VALUE entry = cache->entries[mid]; + int cmp = rstring_cache_cmp(str, length, entry); + + if (cmp == 0) { + return entry; + } else if (cmp > 0) { + low = mid + 1; + } else { + high = mid - 1; + } + } + + VALUE rstring = build_interned_string(str, length); + + if (cache->length < JSON_RVALUE_CACHE_CAPA) { + rvalue_cache_insert_at(cache, low, rstring); + } + return rstring; +} + +static VALUE rsymbol_cache_fetch(rvalue_cache *cache, const char *str, const long length) +{ + int low = 0; + int high = cache->length - 1; + + while (low <= high) { + int mid = (high + low) >> 1; + VALUE entry = cache->entries[mid]; + int cmp = rstring_cache_cmp(str, length, rb_sym2str(entry)); + + if (cmp == 0) { + return entry; + } else if (cmp > 0) { + low = mid + 1; + } else { + high = mid - 1; + } + } + + VALUE rsymbol = build_symbol(str, length); + + if (cache->length < JSON_RVALUE_CACHE_CAPA) { + rvalue_cache_insert_at(cache, low, rsymbol); + } + return rsymbol; +} + +/* rvalue stack */ + +#define RVALUE_STACK_INITIAL_CAPA 128 + +enum rvalue_stack_type { + RVALUE_STACK_HEAP_ALLOCATED = 0, + RVALUE_STACK_STACK_ALLOCATED = 1, }; -static UTF32 unescape_unicode(const unsigned char *p) +typedef struct rvalue_stack_struct { + enum rvalue_stack_type type; + long capa; + long head; + VALUE *ptr; +} rvalue_stack; + +static rvalue_stack *rvalue_stack_spill(rvalue_stack *old_stack, VALUE *handle, rvalue_stack **stack_ref); + +static rvalue_stack *rvalue_stack_grow(rvalue_stack *stack, VALUE *handle, rvalue_stack **stack_ref) { - signed char b; - UTF32 result = 0; - b = digit_values[p[0]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - b = digit_values[p[1]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - b = digit_values[p[2]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - b = digit_values[p[3]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - return result; + long required = stack->capa * 2; + + if (stack->type == RVALUE_STACK_STACK_ALLOCATED) { + stack = rvalue_stack_spill(stack, handle, stack_ref); + } else { + REALLOC_N(stack->ptr, VALUE, required); + stack->capa = required; + } + return stack; } -static int convert_UTF32_to_UTF8(char *buf, UTF32 ch) +static VALUE rvalue_stack_push(rvalue_stack *stack, VALUE value, VALUE *handle, rvalue_stack **stack_ref) +{ + if (RB_UNLIKELY(stack->head >= stack->capa)) { + stack = rvalue_stack_grow(stack, handle, stack_ref); + } + stack->ptr[stack->head] = value; + stack->head++; + return value; +} + +static inline VALUE *rvalue_stack_peek(rvalue_stack *stack, long count) +{ + return stack->ptr + (stack->head - count); +} + +static inline void rvalue_stack_pop(rvalue_stack *stack, long count) +{ + stack->head -= count; +} + +static void rvalue_stack_mark(void *ptr) +{ + rvalue_stack *stack = (rvalue_stack *)ptr; + long index; + for (index = 0; index < stack->head; index++) { + rb_gc_mark(stack->ptr[index]); + } +} + +static void rvalue_stack_free(void *ptr) +{ + rvalue_stack *stack = (rvalue_stack *)ptr; + if (stack) { + ruby_xfree(stack->ptr); + ruby_xfree(stack); + } +} + +static size_t rvalue_stack_memsize(const void *ptr) +{ + const rvalue_stack *stack = (const rvalue_stack *)ptr; + return sizeof(rvalue_stack) + sizeof(VALUE) * stack->capa; +} + +static const rb_data_type_t JSON_Parser_rvalue_stack_type = { + "JSON::Ext::Parser/rvalue_stack", + { + .dmark = rvalue_stack_mark, + .dfree = rvalue_stack_free, + .dsize = rvalue_stack_memsize, + }, + 0, 0, + RUBY_TYPED_FREE_IMMEDIATELY, +}; + +static rvalue_stack *rvalue_stack_spill(rvalue_stack *old_stack, VALUE *handle, rvalue_stack **stack_ref) +{ + rvalue_stack *stack; + *handle = TypedData_Make_Struct(0, rvalue_stack, &JSON_Parser_rvalue_stack_type, stack); + *stack_ref = stack; + MEMCPY(stack, old_stack, rvalue_stack, 1); + + stack->capa = old_stack->capa << 1; + stack->ptr = ALLOC_N(VALUE, stack->capa); + stack->type = RVALUE_STACK_HEAP_ALLOCATED; + MEMCPY(stack->ptr, old_stack->ptr, VALUE, old_stack->head); + return stack; +} + +static void rvalue_stack_eagerly_release(VALUE handle) +{ + if (handle) { + rvalue_stack *stack; + TypedData_Get_Struct(handle, rvalue_stack, &JSON_Parser_rvalue_stack_type, stack); + RTYPEDDATA_DATA(handle) = NULL; + rvalue_stack_free(stack); + } +} + +static int convert_UTF32_to_UTF8(char *buf, uint32_t ch) { int len = 1; if (ch <= 0x7F) { @@ -89,1677 +321,1079 @@ static int convert_UTF32_to_UTF8(char *buf, UTF32 ch) return len; } -static VALUE mJSON, mExt, cParser, eParserError, eNestingError; -static VALUE CNaN, CInfinity, CMinusInfinity; +enum duplicate_key_action { + JSON_DEPRECATED = 0, + JSON_IGNORE, + JSON_RAISE, +}; -static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, - i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, - i_object_class, i_array_class, i_decimal_class, i_key_p, - i_deep_const_get, i_match, i_match_string, i_aset, i_aref, - i_leftshift, i_new, i_try_convert, i_freeze, i_uminus; +typedef struct JSON_ParserStruct { + VALUE on_load_proc; + VALUE decimal_class; + ID decimal_method_id; + enum duplicate_key_action on_duplicate_key; + int max_nesting; + bool allow_nan; + bool allow_trailing_comma; + bool allow_control_characters; + bool symbolize_names; + bool freeze; +} JSON_ParserConfig; + +typedef struct JSON_ParserStateStruct { + VALUE stack_handle; + const char *start; + const char *cursor; + const char *end; + rvalue_stack *stack; + rvalue_cache name_cache; + int in_array; + int current_nesting; +} JSON_ParserState; + +static inline size_t rest(JSON_ParserState *state) { + return state->end - state->cursor; +} +static inline bool eos(JSON_ParserState *state) { + return state->cursor >= state->end; +} -#line 125 "parser.rl" +static inline char peek(JSON_ParserState *state) +{ + if (RB_UNLIKELY(eos(state))) { + return 0; + } + return *state->cursor; +} +static void cursor_position(JSON_ParserState *state, long *line_out, long *column_out) +{ + const char *cursor = state->cursor; + long column = 0; + long line = 1; + + while (cursor >= state->start) { + if (*cursor-- == '\n') { + break; + } + column++; + } + + while (cursor >= state->start) { + if (*cursor-- == '\n') { + line++; + } + } + *line_out = line; + *column_out = column; +} + +static void emit_parse_warning(const char *message, JSON_ParserState *state) +{ + long line, column; + cursor_position(state, &line, &column); + + VALUE warning = rb_sprintf("%s at line %ld column %ld", message, line, column); + rb_funcall(mJSON, rb_intern("deprecation_warning"), 1, warning); +} + +#define PARSE_ERROR_FRAGMENT_LEN 32 + +#ifdef RBIMPL_ATTR_NORETURN +RBIMPL_ATTR_NORETURN() +#endif +static void raise_parse_error(const char *format, JSON_ParserState *state) +{ + unsigned char buffer[PARSE_ERROR_FRAGMENT_LEN + 3]; + long line, column; + cursor_position(state, &line, &column); + + const char *ptr = "EOF"; + if (state->cursor && state->cursor < state->end) { + ptr = state->cursor; + size_t len = 0; + while (len < PARSE_ERROR_FRAGMENT_LEN) { + char ch = ptr[len]; + if (!ch || ch == '\n' || ch == ' ' || ch == '\t' || ch == '\r') { + break; + } + len++; + } + if (len) { + buffer[0] = '\''; + MEMCPY(buffer + 1, ptr, char, len); -#line 107 "parser.c" -enum {JSON_object_start = 1}; -enum {JSON_object_first_final = 27}; -enum {JSON_object_error = 0}; + while (buffer[len] >= 0x80 && buffer[len] < 0xC0) { // Is continuation byte + len--; + } -enum {JSON_object_en_main = 1}; + if (buffer[len] >= 0xC0) { // multibyte character start + len--; + } + buffer[len + 1] = '\''; + buffer[len + 2] = '\0'; + ptr = (const char *)buffer; + } + } -#line 167 "parser.rl" + VALUE msg = rb_sprintf(format, ptr); + VALUE message = rb_enc_sprintf(enc_utf8, "%s at line %ld column %ld", RSTRING_PTR(msg), line, column); + RB_GC_GUARD(msg); + VALUE exc = rb_exc_new_str(rb_path2class("JSON::ParserError"), message); + rb_ivar_set(exc, rb_intern("@line"), LONG2NUM(line)); + rb_ivar_set(exc, rb_intern("@column"), LONG2NUM(column)); + rb_exc_raise(exc); +} -static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) +#ifdef RBIMPL_ATTR_NORETURN +RBIMPL_ATTR_NORETURN() +#endif +static void raise_parse_error_at(const char *format, JSON_ParserState *state, const char *at) { - int cs = EVIL; - VALUE last_name = Qnil; - VALUE object_class = json->object_class; + state->cursor = at; + raise_parse_error(format, state); +} + +/* unicode */ - if (json->max_nesting && current_nesting > json->max_nesting) { - rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); +static const signed char digit_values[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, + -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1 +}; + +static uint32_t unescape_unicode(JSON_ParserState *state, const char *sp, const char *spe) +{ + if (RB_UNLIKELY(sp > spe - 4)) { + raise_parse_error_at("incomplete unicode character escape sequence at %s", state, sp - 2); } - *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class); - - -#line 131 "parser.c" - { - cs = JSON_object_start; - } - -#line 182 "parser.rl" - -#line 138 "parser.c" - { - if ( p == pe ) - goto _test_eof; - switch ( cs ) - { -case 1: - if ( (*p) == 123 ) - goto st2; - goto st0; -st0: -cs = 0; - goto _out; -st2: - if ( ++p == pe ) - goto _test_eof2; -case 2: - switch( (*p) ) { - case 13: goto st2; - case 32: goto st2; - case 34: goto tr2; - case 47: goto st23; - case 125: goto tr4; - } - if ( 9 <= (*p) && (*p) <= 10 ) - goto st2; - goto st0; -tr2: -#line 149 "parser.rl" - { - char *np; - json->parsing_name = 1; - np = JSON_parse_string(json, p, pe, &last_name); - json->parsing_name = 0; - if (np == NULL) { p--; {p++; cs = 3; goto _out;} } else {p = (( np))-1;} + const unsigned char *p = (const unsigned char *)sp; + + const signed char b0 = digit_values[p[0]]; + const signed char b1 = digit_values[p[1]]; + const signed char b2 = digit_values[p[2]]; + const signed char b3 = digit_values[p[3]]; + + if (RB_UNLIKELY((signed char)(b0 | b1 | b2 | b3) < 0)) { + raise_parse_error_at("incomplete unicode character escape sequence at %s", state, sp - 2); } - goto st3; -st3: - if ( ++p == pe ) - goto _test_eof3; -case 3: -#line 179 "parser.c" - switch( (*p) ) { - case 13: goto st3; - case 32: goto st3; - case 47: goto st4; - case 58: goto st8; - } - if ( 9 <= (*p) && (*p) <= 10 ) - goto st3; - goto st0; -st4: - if ( ++p == pe ) - goto _test_eof4; -case 4: - switch( (*p) ) { - case 42: goto st5; - case 47: goto st7; - } - goto st0; -st5: - if ( ++p == pe ) - goto _test_eof5; -case 5: - if ( (*p) == 42 ) - goto st6; - goto st5; -st6: - if ( ++p == pe ) - goto _test_eof6; -case 6: - switch( (*p) ) { - case 42: goto st6; - case 47: goto st3; - } - goto st5; -st7: - if ( ++p == pe ) - goto _test_eof7; -case 7: - if ( (*p) == 10 ) - goto st3; - goto st7; -st8: - if ( ++p == pe ) - goto _test_eof8; -case 8: - switch( (*p) ) { - case 13: goto st8; - case 32: goto st8; - case 34: goto tr11; - case 45: goto tr11; - case 47: goto st19; - case 73: goto tr11; - case 78: goto tr11; - case 91: goto tr11; - case 102: goto tr11; - case 110: goto tr11; - case 116: goto tr11; - case 123: goto tr11; - } - if ( (*p) > 10 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto tr11; - } else if ( (*p) >= 9 ) - goto st8; - goto st0; -tr11: -#line 133 "parser.rl" - { - VALUE v = Qnil; - char *np = JSON_parse_value(json, p, pe, &v, current_nesting); - if (np == NULL) { - p--; {p++; cs = 9; goto _out;} - } else { - if (NIL_P(json->object_class)) { - OBJ_FREEZE(last_name); - rb_hash_aset(*result, last_name, v); + + return ((uint32_t)b0 << 12) | ((uint32_t)b1 << 8) | ((uint32_t)b2 << 4) | (uint32_t)b3; +} + +#define GET_PARSER_CONFIG \ + JSON_ParserConfig *config; \ + TypedData_Get_Struct(self, JSON_ParserConfig, &JSON_ParserConfig_type, config) + +static const rb_data_type_t JSON_ParserConfig_type; + +static void +json_eat_comments(JSON_ParserState *state) +{ + const char *start = state->cursor; + state->cursor++; + + switch (peek(state)) { + case '/': { + state->cursor = memchr(state->cursor, '\n', state->end - state->cursor); + if (!state->cursor) { + state->cursor = state->end; } else { - rb_funcall(*result, i_aset, 2, last_name, v); + state->cursor++; } - {p = (( np))-1;} + break; } - } - goto st9; -st9: - if ( ++p == pe ) - goto _test_eof9; -case 9: -#line 267 "parser.c" - switch( (*p) ) { - case 13: goto st9; - case 32: goto st9; - case 44: goto st10; - case 47: goto st15; - case 125: goto tr4; - } - if ( 9 <= (*p) && (*p) <= 10 ) - goto st9; - goto st0; -st10: - if ( ++p == pe ) - goto _test_eof10; -case 10: - switch( (*p) ) { - case 13: goto st10; - case 32: goto st10; - case 34: goto tr2; - case 47: goto st11; - } - if ( 9 <= (*p) && (*p) <= 10 ) - goto st10; - goto st0; -st11: - if ( ++p == pe ) - goto _test_eof11; -case 11: - switch( (*p) ) { - case 42: goto st12; - case 47: goto st14; - } - goto st0; -st12: - if ( ++p == pe ) - goto _test_eof12; -case 12: - if ( (*p) == 42 ) - goto st13; - goto st12; -st13: - if ( ++p == pe ) - goto _test_eof13; -case 13: - switch( (*p) ) { - case 42: goto st13; - case 47: goto st10; - } - goto st12; -st14: - if ( ++p == pe ) - goto _test_eof14; -case 14: - if ( (*p) == 10 ) - goto st10; - goto st14; -st15: - if ( ++p == pe ) - goto _test_eof15; -case 15: - switch( (*p) ) { - case 42: goto st16; - case 47: goto st18; - } - goto st0; -st16: - if ( ++p == pe ) - goto _test_eof16; -case 16: - if ( (*p) == 42 ) - goto st17; - goto st16; -st17: - if ( ++p == pe ) - goto _test_eof17; -case 17: - switch( (*p) ) { - case 42: goto st17; - case 47: goto st9; - } - goto st16; -st18: - if ( ++p == pe ) - goto _test_eof18; -case 18: - if ( (*p) == 10 ) - goto st9; - goto st18; -tr4: -#line 157 "parser.rl" - { p--; {p++; cs = 27; goto _out;} } - goto st27; -st27: - if ( ++p == pe ) - goto _test_eof27; -case 27: -#line 363 "parser.c" - goto st0; -st19: - if ( ++p == pe ) - goto _test_eof19; -case 19: - switch( (*p) ) { - case 42: goto st20; - case 47: goto st22; - } - goto st0; -st20: - if ( ++p == pe ) - goto _test_eof20; -case 20: - if ( (*p) == 42 ) - goto st21; - goto st20; -st21: - if ( ++p == pe ) - goto _test_eof21; -case 21: - switch( (*p) ) { - case 42: goto st21; - case 47: goto st8; - } - goto st20; -st22: - if ( ++p == pe ) - goto _test_eof22; -case 22: - if ( (*p) == 10 ) - goto st8; - goto st22; -st23: - if ( ++p == pe ) - goto _test_eof23; -case 23: - switch( (*p) ) { - case 42: goto st24; - case 47: goto st26; - } - goto st0; -st24: - if ( ++p == pe ) - goto _test_eof24; -case 24: - if ( (*p) == 42 ) - goto st25; - goto st24; -st25: - if ( ++p == pe ) - goto _test_eof25; -case 25: - switch( (*p) ) { - case 42: goto st25; - case 47: goto st2; - } - goto st24; -st26: - if ( ++p == pe ) - goto _test_eof26; -case 26: - if ( (*p) == 10 ) - goto st2; - goto st26; - } - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof11: cs = 11; goto _test_eof; - _test_eof12: cs = 12; goto _test_eof; - _test_eof13: cs = 13; goto _test_eof; - _test_eof14: cs = 14; goto _test_eof; - _test_eof15: cs = 15; goto _test_eof; - _test_eof16: cs = 16; goto _test_eof; - _test_eof17: cs = 17; goto _test_eof; - _test_eof18: cs = 18; goto _test_eof; - _test_eof27: cs = 27; goto _test_eof; - _test_eof19: cs = 19; goto _test_eof; - _test_eof20: cs = 20; goto _test_eof; - _test_eof21: cs = 21; goto _test_eof; - _test_eof22: cs = 22; goto _test_eof; - _test_eof23: cs = 23; goto _test_eof; - _test_eof24: cs = 24; goto _test_eof; - _test_eof25: cs = 25; goto _test_eof; - _test_eof26: cs = 26; goto _test_eof; - - _test_eof: {} - _out: {} - } - -#line 183 "parser.rl" - - if (cs >= JSON_object_first_final) { - if (json->create_additions) { - VALUE klassname; - if (NIL_P(json->object_class)) { - klassname = rb_hash_aref(*result, json->create_id); - } else { - klassname = rb_funcall(*result, i_aref, 1, json->create_id); - } - if (!NIL_P(klassname)) { - VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname); - if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) { - *result = rb_funcall(klass, i_json_create, 1, *result); + case '*': { + state->cursor++; + + while (true) { + const char *next_match = memchr(state->cursor, '*', state->end - state->cursor); + if (!next_match) { + raise_parse_error_at("unterminated comment, expected closing '*/'", state, start); + } + + state->cursor = next_match + 1; + if (peek(state) == '/') { + state->cursor++; + break; } } + break; } - return p + 1; - } else { - return NULL; + default: + raise_parse_error_at("unexpected token %s", state, start); + break; } } +ALWAYS_INLINE(static) void +json_eat_whitespace(JSON_ParserState *state) +{ + while (true) { + switch (peek(state)) { + case ' ': + state->cursor++; + break; + case '\n': + state->cursor++; + + // Heuristic: if we see a newline, there is likely consecutive spaces after it. +#if JSON_CPU_LITTLE_ENDIAN_64BITS + while (rest(state) > 8) { + uint64_t chunk; + memcpy(&chunk, state->cursor, sizeof(uint64_t)); + if (chunk == 0x2020202020202020) { + state->cursor += 8; + continue; + } + uint32_t consecutive_spaces = trailing_zeros64(chunk ^ 0x2020202020202020) / CHAR_BIT; + state->cursor += consecutive_spaces; + break; + } +#endif + break; + case '\t': + case '\r': + state->cursor++; + break; + case '/': + json_eat_comments(state); + break; + + default: + return; + } + } +} -#line 486 "parser.c" -enum {JSON_value_start = 1}; -enum {JSON_value_first_final = 29}; -enum {JSON_value_error = 0}; +static inline VALUE build_string(const char *start, const char *end, bool intern, bool symbolize) +{ + if (symbolize) { + intern = true; + } + VALUE result; +# ifdef HAVE_RB_ENC_INTERNED_STR + if (intern) { + result = rb_enc_interned_str(start, (long)(end - start), enc_utf8); + } else { + result = rb_utf8_str_new(start, (long)(end - start)); + } +# else + result = rb_utf8_str_new(start, (long)(end - start)); + if (intern) { + result = rb_funcall(rb_str_freeze(result), i_uminus, 0); + } +# endif + + if (symbolize) { + result = rb_str_intern(result); + } + + return result; +} + +static inline bool json_string_cacheable_p(const char *string, size_t length) +{ + // We mostly want to cache strings that are likely to be repeated. + // Simple heuristics: + // - Common names aren't likely to be very long. So we just don't cache names above an arbitrary threshold. + // - If the first character isn't a letter, we're much less likely to see this string again. + return length <= JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH && rb_isalpha(string[0]); +} + +static inline VALUE json_string_fastpath(JSON_ParserState *state, JSON_ParserConfig *config, const char *string, const char *stringEnd, bool is_name) +{ + bool intern = is_name || config->freeze; + bool symbolize = is_name && config->symbolize_names; + size_t bufferSize = stringEnd - string; -enum {JSON_value_en_main = 1}; + if (is_name && state->in_array && RB_LIKELY(json_string_cacheable_p(string, bufferSize))) { + VALUE cached_key; + if (RB_UNLIKELY(symbolize)) { + cached_key = rsymbol_cache_fetch(&state->name_cache, string, bufferSize); + } else { + cached_key = rstring_cache_fetch(&state->name_cache, string, bufferSize); + } + if (RB_LIKELY(cached_key)) { + return cached_key; + } + } -#line 283 "parser.rl" + return build_string(string, stringEnd, intern, symbolize); +} +#define JSON_MAX_UNESCAPE_POSITIONS 16 +typedef struct _json_unescape_positions { + long size; + const char **positions; + unsigned long additional_backslashes; +} JSON_UnescapePositions; -static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) +static inline const char *json_next_backslash(const char *pe, const char *stringEnd, JSON_UnescapePositions *positions) { - int cs = EVIL; - - -#line 502 "parser.c" - { - cs = JSON_value_start; - } - -#line 290 "parser.rl" - -#line 509 "parser.c" - { - if ( p == pe ) - goto _test_eof; - switch ( cs ) - { -st1: - if ( ++p == pe ) - goto _test_eof1; -case 1: - switch( (*p) ) { - case 13: goto st1; - case 32: goto st1; - case 34: goto tr2; - case 45: goto tr3; - case 47: goto st6; - case 73: goto st10; - case 78: goto st17; - case 91: goto tr7; - case 102: goto st19; - case 110: goto st23; - case 116: goto st26; - case 123: goto tr11; - } - if ( (*p) > 10 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto tr3; - } else if ( (*p) >= 9 ) - goto st1; - goto st0; -st0: -cs = 0; - goto _out; -tr2: -#line 235 "parser.rl" - { - char *np = JSON_parse_string(json, p, pe, result); - if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} + while (positions->size) { + positions->size--; + const char *next_position = positions->positions[0]; + positions->positions++; + if (next_position >= pe) { + return next_position; + } } - goto st29; -tr3: -#line 240 "parser.rl" - { - char *np; - if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) { - if (json->allow_nan) { - *result = CMinusInfinity; - {p = (( p + 10))-1;} - p--; {p++; cs = 29; goto _out;} - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); + + if (positions->additional_backslashes) { + positions->additional_backslashes--; + return memchr(pe, '\\', stringEnd - pe); + } + + return NULL; +} + +NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_ParserConfig *config, const char *string, const char *stringEnd, bool is_name, JSON_UnescapePositions *positions) +{ + bool intern = is_name || config->freeze; + bool symbolize = is_name && config->symbolize_names; + size_t bufferSize = stringEnd - string; + const char *p = string, *pe = string, *bufferStart; + char *buffer; + + VALUE result = rb_str_buf_new(bufferSize); + rb_enc_associate_index(result, utf8_encindex); + buffer = RSTRING_PTR(result); + bufferStart = buffer; + +#define APPEND_CHAR(chr) *buffer++ = chr; p = ++pe; + + while (pe < stringEnd && (pe = json_next_backslash(pe, stringEnd, positions))) { + if (pe > p) { + MEMCPY(buffer, p, char, pe - p); + buffer += pe - p; + } + switch (*++pe) { + case '"': + case '/': + p = pe; // nothing to unescape just need to skip the backslash + break; + case '\\': + APPEND_CHAR('\\'); + break; + case 'n': + APPEND_CHAR('\n'); + break; + case 'r': + APPEND_CHAR('\r'); + break; + case 't': + APPEND_CHAR('\t'); + break; + case 'b': + APPEND_CHAR('\b'); + break; + case 'f': + APPEND_CHAR('\f'); + break; + case 'u': { + uint32_t ch = unescape_unicode(state, ++pe, stringEnd); + pe += 3; + /* To handle values above U+FFFF, we take a sequence of + * \uXXXX escapes in the U+D800..U+DBFF then + * U+DC00..U+DFFF ranges, take the low 10 bits from each + * to make a 20-bit number, then add 0x10000 to get the + * final codepoint. + * + * See Unicode 15: 3.8 "Surrogates", 5.3 "Handling + * Surrogate Pairs in UTF-16", and 23.6 "Surrogates + * Area". + */ + if ((ch & 0xFC00) == 0xD800) { + pe++; + if (RB_LIKELY((pe <= stringEnd - 6) && memcmp(pe, "\\u", 2) == 0)) { + uint32_t sur = unescape_unicode(state, pe + 2, stringEnd); + + if (RB_UNLIKELY((sur & 0xFC00) != 0xDC00)) { + raise_parse_error_at("invalid surrogate pair at %s", state, p); + } + + ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) | (sur & 0x3FF)); + pe += 5; + } else { + raise_parse_error_at("incomplete surrogate pair at %s", state, p); + break; + } + } + + int unescape_len = convert_UTF32_to_UTF8(buffer, ch); + buffer += unescape_len; + p = ++pe; + break; } + default: + if ((unsigned char)*pe < 0x20) { + if (!config->allow_control_characters) { + if (*pe == '\n') { + raise_parse_error_at("Invalid unescaped newline character (\\n) in string: %s", state, pe - 1); + } + raise_parse_error_at("invalid ASCII control character in string: %s", state, pe - 1); + } + } else { + raise_parse_error_at("invalid escape character in string: %s", state, pe - 1); + } + break; } - np = JSON_parse_float(json, p, pe, result); - if (np != NULL) {p = (( np))-1;} - np = JSON_parse_integer(json, p, pe, result); - if (np != NULL) {p = (( np))-1;} - p--; {p++; cs = 29; goto _out;} - } - goto st29; -tr7: -#line 258 "parser.rl" - { - char *np; - np = JSON_parse_array(json, p, pe, result, current_nesting + 1); - if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} } - goto st29; -tr11: -#line 264 "parser.rl" - { - char *np; - np = JSON_parse_object(json, p, pe, result, current_nesting + 1); - if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} +#undef APPEND_CHAR + + if (stringEnd > p) { + MEMCPY(buffer, p, char, stringEnd - p); + buffer += stringEnd - p; } - goto st29; -tr25: -#line 228 "parser.rl" - { - if (json->allow_nan) { - *result = CInfinity; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 7); - } + rb_str_set_len(result, buffer - bufferStart); + + if (symbolize) { + result = rb_str_intern(result); + } else if (intern) { + result = rb_str_to_interned_str(result); } - goto st29; -tr27: -#line 221 "parser.rl" - { - if (json->allow_nan) { - *result = CNaN; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 2); + + return result; +} + +#define MAX_FAST_INTEGER_SIZE 18 + +static VALUE json_decode_large_integer(const char *start, long len) +{ + VALUE buffer_v; + char *buffer = RB_ALLOCV_N(char, buffer_v, len + 1); + MEMCPY(buffer, start, char, len); + buffer[len] = '\0'; + VALUE number = rb_cstr2inum(buffer, 10); + RB_ALLOCV_END(buffer_v); + return number; +} + +static inline VALUE +json_decode_integer(uint64_t mantissa, int mantissa_digits, bool negative, const char *start, const char *end) +{ + if (RB_LIKELY(mantissa_digits < MAX_FAST_INTEGER_SIZE)) { + if (negative) { + return INT64T2NUM(-((int64_t)mantissa)); } + return UINT64T2NUM(mantissa); } - goto st29; -tr31: -#line 215 "parser.rl" - { - *result = Qfalse; + + return json_decode_large_integer(start, end - start); +} + +static VALUE json_decode_large_float(const char *start, long len) +{ + if (RB_LIKELY(len < 64)) { + char buffer[64]; + MEMCPY(buffer, start, char, len); + buffer[len] = '\0'; + return DBL2NUM(rb_cstr_to_dbl(buffer, 1)); } - goto st29; -tr34: -#line 212 "parser.rl" - { - *result = Qnil; + + VALUE buffer_v; + char *buffer = RB_ALLOCV_N(char, buffer_v, len + 1); + MEMCPY(buffer, start, char, len); + buffer[len] = '\0'; + VALUE number = DBL2NUM(rb_cstr_to_dbl(buffer, 1)); + RB_ALLOCV_END(buffer_v); + return number; +} + +/* Ruby JSON optimized float decoder using vendored Ryu algorithm + * Accepts pre-extracted mantissa and exponent from first-pass validation + */ +static inline VALUE json_decode_float(JSON_ParserConfig *config, uint64_t mantissa, int mantissa_digits, int32_t exponent, bool negative, + const char *start, const char *end) +{ + if (RB_UNLIKELY(config->decimal_class)) { + VALUE text = rb_str_new(start, end - start); + return rb_funcallv(config->decimal_class, config->decimal_method_id, 1, &text); } - goto st29; -tr37: -#line 218 "parser.rl" - { - *result = Qtrue; + + // Fall back to rb_cstr_to_dbl for potential subnormals (rare edge case) + // Ryu has rounding issues with subnormals around 1e-310 (< 2.225e-308) + if (RB_UNLIKELY(mantissa_digits > 17 || mantissa_digits + exponent < -307)) { + return json_decode_large_float(start, end - start); } - goto st29; -st29: - if ( ++p == pe ) - goto _test_eof29; -case 29: -#line 270 "parser.rl" - { p--; {p++; cs = 29; goto _out;} } -#line 629 "parser.c" - switch( (*p) ) { - case 13: goto st29; - case 32: goto st29; - case 47: goto st2; - } - if ( 9 <= (*p) && (*p) <= 10 ) - goto st29; - goto st0; -st2: - if ( ++p == pe ) - goto _test_eof2; -case 2: - switch( (*p) ) { - case 42: goto st3; - case 47: goto st5; - } - goto st0; -st3: - if ( ++p == pe ) - goto _test_eof3; -case 3: - if ( (*p) == 42 ) - goto st4; - goto st3; -st4: - if ( ++p == pe ) - goto _test_eof4; -case 4: - switch( (*p) ) { - case 42: goto st4; - case 47: goto st29; - } - goto st3; -st5: - if ( ++p == pe ) - goto _test_eof5; -case 5: - if ( (*p) == 10 ) - goto st29; - goto st5; -st6: - if ( ++p == pe ) - goto _test_eof6; -case 6: - switch( (*p) ) { - case 42: goto st7; - case 47: goto st9; - } - goto st0; -st7: - if ( ++p == pe ) - goto _test_eof7; -case 7: - if ( (*p) == 42 ) - goto st8; - goto st7; -st8: - if ( ++p == pe ) - goto _test_eof8; -case 8: - switch( (*p) ) { - case 42: goto st8; - case 47: goto st1; - } - goto st7; -st9: - if ( ++p == pe ) - goto _test_eof9; -case 9: - if ( (*p) == 10 ) - goto st1; - goto st9; -st10: - if ( ++p == pe ) - goto _test_eof10; -case 10: - if ( (*p) == 110 ) - goto st11; - goto st0; -st11: - if ( ++p == pe ) - goto _test_eof11; -case 11: - if ( (*p) == 102 ) - goto st12; - goto st0; -st12: - if ( ++p == pe ) - goto _test_eof12; -case 12: - if ( (*p) == 105 ) - goto st13; - goto st0; -st13: - if ( ++p == pe ) - goto _test_eof13; -case 13: - if ( (*p) == 110 ) - goto st14; - goto st0; -st14: - if ( ++p == pe ) - goto _test_eof14; -case 14: - if ( (*p) == 105 ) - goto st15; - goto st0; -st15: - if ( ++p == pe ) - goto _test_eof15; -case 15: - if ( (*p) == 116 ) - goto st16; - goto st0; -st16: - if ( ++p == pe ) - goto _test_eof16; -case 16: - if ( (*p) == 121 ) - goto tr25; - goto st0; -st17: - if ( ++p == pe ) - goto _test_eof17; -case 17: - if ( (*p) == 97 ) - goto st18; - goto st0; -st18: - if ( ++p == pe ) - goto _test_eof18; -case 18: - if ( (*p) == 78 ) - goto tr27; - goto st0; -st19: - if ( ++p == pe ) - goto _test_eof19; -case 19: - if ( (*p) == 97 ) - goto st20; - goto st0; -st20: - if ( ++p == pe ) - goto _test_eof20; -case 20: - if ( (*p) == 108 ) - goto st21; - goto st0; -st21: - if ( ++p == pe ) - goto _test_eof21; -case 21: - if ( (*p) == 115 ) - goto st22; - goto st0; -st22: - if ( ++p == pe ) - goto _test_eof22; -case 22: - if ( (*p) == 101 ) - goto tr31; - goto st0; -st23: - if ( ++p == pe ) - goto _test_eof23; -case 23: - if ( (*p) == 117 ) - goto st24; - goto st0; -st24: - if ( ++p == pe ) - goto _test_eof24; -case 24: - if ( (*p) == 108 ) - goto st25; - goto st0; -st25: - if ( ++p == pe ) - goto _test_eof25; -case 25: - if ( (*p) == 108 ) - goto tr34; - goto st0; -st26: - if ( ++p == pe ) - goto _test_eof26; -case 26: - if ( (*p) == 114 ) - goto st27; - goto st0; -st27: - if ( ++p == pe ) - goto _test_eof27; -case 27: - if ( (*p) == 117 ) - goto st28; - goto st0; -st28: - if ( ++p == pe ) - goto _test_eof28; -case 28: - if ( (*p) == 101 ) - goto tr37; - goto st0; - } - _test_eof1: cs = 1; goto _test_eof; - _test_eof29: cs = 29; goto _test_eof; - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof11: cs = 11; goto _test_eof; - _test_eof12: cs = 12; goto _test_eof; - _test_eof13: cs = 13; goto _test_eof; - _test_eof14: cs = 14; goto _test_eof; - _test_eof15: cs = 15; goto _test_eof; - _test_eof16: cs = 16; goto _test_eof; - _test_eof17: cs = 17; goto _test_eof; - _test_eof18: cs = 18; goto _test_eof; - _test_eof19: cs = 19; goto _test_eof; - _test_eof20: cs = 20; goto _test_eof; - _test_eof21: cs = 21; goto _test_eof; - _test_eof22: cs = 22; goto _test_eof; - _test_eof23: cs = 23; goto _test_eof; - _test_eof24: cs = 24; goto _test_eof; - _test_eof25: cs = 25; goto _test_eof; - _test_eof26: cs = 26; goto _test_eof; - _test_eof27: cs = 27; goto _test_eof; - _test_eof28: cs = 28; goto _test_eof; - - _test_eof: {} - _out: {} - } - -#line 291 "parser.rl" - - if (json->freeze) { - OBJ_FREEZE(*result); + + return DBL2NUM(ryu_s2d_from_parts(mantissa, mantissa_digits, exponent, negative)); +} + +static inline VALUE json_decode_array(JSON_ParserState *state, JSON_ParserConfig *config, long count) +{ + VALUE array = rb_ary_new_from_values(count, rvalue_stack_peek(state->stack, count)); + rvalue_stack_pop(state->stack, count); + + if (config->freeze) { + RB_OBJ_FREEZE(array); } - if (cs >= JSON_value_first_final) { - return p; - } else { - return NULL; + return array; +} + +static VALUE json_find_duplicated_key(size_t count, const VALUE *pairs) +{ + VALUE set = rb_hash_new_capa(count / 2); + for (size_t index = 0; index < count; index += 2) { + size_t before = RHASH_SIZE(set); + VALUE key = pairs[index]; + rb_hash_aset(set, key, Qtrue); + if (RHASH_SIZE(set) == before) { + if (RB_SYMBOL_P(key)) { + return rb_sym2str(key); + } + return key; + } } + return Qfalse; +} + +static void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_key) +{ + VALUE message = rb_sprintf( + "detected duplicate key %"PRIsVALUE" in JSON object. This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`", + rb_inspect(duplicate_key) + ); + + emit_parse_warning(RSTRING_PTR(message), state); + RB_GC_GUARD(message); } +#ifdef RBIMPL_ATTR_NORETURN +RBIMPL_ATTR_NORETURN() +#endif +static void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_key) +{ + VALUE message = rb_sprintf( + "duplicate key %"PRIsVALUE, + rb_inspect(duplicate_key) + ); -#line 884 "parser.c" -enum {JSON_integer_start = 1}; -enum {JSON_integer_first_final = 3}; -enum {JSON_integer_error = 0}; + raise_parse_error(RSTRING_PTR(message), state); + RB_GC_GUARD(message); +} -enum {JSON_integer_en_main = 1}; +static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfig *config, size_t count) +{ + size_t entries_count = count / 2; + VALUE object = rb_hash_new_capa(entries_count); + const VALUE *pairs = rvalue_stack_peek(state->stack, count); + rb_hash_bulk_insert(count, pairs, object); + + if (RB_UNLIKELY(RHASH_SIZE(object) < entries_count)) { + switch (config->on_duplicate_key) { + case JSON_IGNORE: + break; + case JSON_DEPRECATED: + emit_duplicate_key_warning(state, json_find_duplicated_key(count, pairs)); + break; + case JSON_RAISE: + raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs)); + break; + } + } + rvalue_stack_pop(state->stack, count); -#line 311 "parser.rl" + if (config->freeze) { + RB_OBJ_FREEZE(object); + } + return object; +} -static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result) +static inline VALUE json_push_value(JSON_ParserState *state, JSON_ParserConfig *config, VALUE value) { - int cs = EVIL; - - -#line 900 "parser.c" - { - cs = JSON_integer_start; - } - -#line 318 "parser.rl" - json->memo = p; - -#line 908 "parser.c" - { - if ( p == pe ) - goto _test_eof; - switch ( cs ) - { -case 1: - switch( (*p) ) { - case 45: goto st2; - case 48: goto st3; - } - if ( 49 <= (*p) && (*p) <= 57 ) - goto st5; - goto st0; -st0: -cs = 0; - goto _out; -st2: - if ( ++p == pe ) - goto _test_eof2; -case 2: - if ( (*p) == 48 ) - goto st3; - if ( 49 <= (*p) && (*p) <= 57 ) - goto st5; - goto st0; -st3: - if ( ++p == pe ) - goto _test_eof3; -case 3: - if ( 48 <= (*p) && (*p) <= 57 ) - goto st0; - goto tr4; -tr4: -#line 308 "parser.rl" - { p--; {p++; cs = 4; goto _out;} } - goto st4; -st4: - if ( ++p == pe ) - goto _test_eof4; -case 4: -#line 949 "parser.c" - goto st0; -st5: - if ( ++p == pe ) - goto _test_eof5; -case 5: - if ( 48 <= (*p) && (*p) <= 57 ) - goto st5; - goto tr4; - } - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - - _test_eof: {} - _out: {} - } - -#line 320 "parser.rl" - - if (cs >= JSON_integer_first_final) { - long len = p - json->memo; - fbuffer_clear(json->fbuffer); - fbuffer_append(json->fbuffer, json->memo, len); - fbuffer_append_char(json->fbuffer, '\0'); - *result = rb_cstr2inum(FBUFFER_PTR(json->fbuffer), 10); - return p + 1; - } else { - return NULL; + if (RB_UNLIKELY(config->on_load_proc)) { + value = rb_proc_call_with_block(config->on_load_proc, 1, &value, Qnil); } + rvalue_stack_push(state->stack, value, &state->stack_handle, &state->stack); + return value; } +static const bool string_scan_table[256] = { + // ASCII Control Characters + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + // ASCII Characters + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // '"' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // '\\' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; -#line 983 "parser.c" -enum {JSON_float_start = 1}; -enum {JSON_float_first_final = 8}; -enum {JSON_float_error = 0}; +#ifdef HAVE_SIMD +static SIMD_Implementation simd_impl = SIMD_NONE; +#endif /* HAVE_SIMD */ -enum {JSON_float_en_main = 1}; +ALWAYS_INLINE(static) bool string_scan(JSON_ParserState *state) +{ +#ifdef HAVE_SIMD +#if defined(HAVE_SIMD_NEON) + uint64_t mask = 0; + if (string_scan_simd_neon(&state->cursor, state->end, &mask)) { + state->cursor += trailing_zeros64(mask) >> 2; + return true; + } -#line 345 "parser.rl" +#elif defined(HAVE_SIMD_SSE2) + if (simd_impl == SIMD_SSE2) { + int mask = 0; + if (string_scan_simd_sse2(&state->cursor, state->end, &mask)) { + state->cursor += trailing_zeros(mask); + return true; + } + } +#endif /* HAVE_SIMD_NEON or HAVE_SIMD_SSE2 */ +#endif /* HAVE_SIMD */ + while (!eos(state)) { + if (RB_UNLIKELY(string_scan_table[(unsigned char)*state->cursor])) { + return true; + } + state->cursor++; + } + return false; +} -static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result) +static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfig *config, bool is_name, const char *start) { - int cs = EVIL; - - -#line 999 "parser.c" - { - cs = JSON_float_start; - } - -#line 352 "parser.rl" - json->memo = p; - -#line 1007 "parser.c" - { - if ( p == pe ) - goto _test_eof; - switch ( cs ) - { -case 1: - switch( (*p) ) { - case 45: goto st2; - case 48: goto st3; - } - if ( 49 <= (*p) && (*p) <= 57 ) - goto st7; - goto st0; -st0: -cs = 0; - goto _out; -st2: - if ( ++p == pe ) - goto _test_eof2; -case 2: - if ( (*p) == 48 ) - goto st3; - if ( 49 <= (*p) && (*p) <= 57 ) - goto st7; - goto st0; -st3: - if ( ++p == pe ) - goto _test_eof3; -case 3: - switch( (*p) ) { - case 46: goto st4; - case 69: goto st5; - case 101: goto st5; - } - goto st0; -st4: - if ( ++p == pe ) - goto _test_eof4; -case 4: - if ( 48 <= (*p) && (*p) <= 57 ) - goto st8; - goto st0; -st8: - if ( ++p == pe ) - goto _test_eof8; -case 8: - switch( (*p) ) { - case 69: goto st5; - case 101: goto st5; - } - if ( (*p) > 46 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto st8; - } else if ( (*p) >= 45 ) - goto st0; - goto tr9; -tr9: -#line 339 "parser.rl" - { p--; {p++; cs = 9; goto _out;} } - goto st9; -st9: - if ( ++p == pe ) - goto _test_eof9; -case 9: -#line 1072 "parser.c" - goto st0; -st5: - if ( ++p == pe ) - goto _test_eof5; -case 5: - switch( (*p) ) { - case 43: goto st6; - case 45: goto st6; - } - if ( 48 <= (*p) && (*p) <= 57 ) - goto st10; - goto st0; -st6: - if ( ++p == pe ) - goto _test_eof6; -case 6: - if ( 48 <= (*p) && (*p) <= 57 ) - goto st10; - goto st0; -st10: - if ( ++p == pe ) - goto _test_eof10; -case 10: - switch( (*p) ) { - case 69: goto st0; - case 101: goto st0; - } - if ( (*p) > 46 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto st10; - } else if ( (*p) >= 45 ) - goto st0; - goto tr9; -st7: - if ( ++p == pe ) - goto _test_eof7; -case 7: - switch( (*p) ) { - case 46: goto st4; - case 69: goto st5; - case 101: goto st5; - } - if ( 48 <= (*p) && (*p) <= 57 ) - goto st7; - goto st0; - } - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - - _test_eof: {} - _out: {} - } - -#line 354 "parser.rl" - - if (cs >= JSON_float_first_final) { - VALUE mod = Qnil; - ID method_id = 0; - if (rb_respond_to(json->decimal_class, i_try_convert)) { - mod = json->decimal_class; - method_id = i_try_convert; - } else if (rb_respond_to(json->decimal_class, i_new)) { - mod = json->decimal_class; - method_id = i_new; - } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) { - VALUE name = rb_class_name(json->decimal_class); - const char *name_cstr = RSTRING_PTR(name); - const char *last_colon = strrchr(name_cstr, ':'); - if (last_colon) { - const char *mod_path_end = last_colon - 1; - VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr); - mod = rb_path_to_class(mod_path); - - const char *method_name_beg = last_colon + 1; - long before_len = method_name_beg - name_cstr; - long len = RSTRING_LEN(name) - before_len; - VALUE method_name = rb_str_substr(name, before_len, len); - method_id = SYM2ID(rb_str_intern(method_name)); - } else { - mod = rb_mKernel; - method_id = SYM2ID(rb_str_intern(name)); + const char *backslashes[JSON_MAX_UNESCAPE_POSITIONS]; + JSON_UnescapePositions positions = { + .size = 0, + .positions = backslashes, + .additional_backslashes = 0, + }; + + do { + switch (*state->cursor) { + case '"': { + VALUE string = json_string_unescape(state, config, start, state->cursor, is_name, &positions); + state->cursor++; + return json_push_value(state, config, string); } + case '\\': { + if (RB_LIKELY(positions.size < JSON_MAX_UNESCAPE_POSITIONS)) { + backslashes[positions.size] = state->cursor; + positions.size++; + } else { + positions.additional_backslashes++; + } + state->cursor++; + break; + } + default: + if (!config->allow_control_characters) { + raise_parse_error("invalid ASCII control character in string: %s", state); + } + break; } - long len = p - json->memo; - fbuffer_clear(json->fbuffer); - fbuffer_append(json->fbuffer, json->memo, len); - fbuffer_append_char(json->fbuffer, '\0'); + state->cursor++; + } while (string_scan(state)); - if (method_id) { - VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer)); - *result = rb_funcallv(mod, method_id, 1, &text); - } else { - *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1)); - } + raise_parse_error("unexpected end of input, expected closing \"", state); + return Qfalse; +} - return p + 1; - } else { - return NULL; +ALWAYS_INLINE(static) VALUE json_parse_string(JSON_ParserState *state, JSON_ParserConfig *config, bool is_name) +{ + state->cursor++; + const char *start = state->cursor; + + if (RB_UNLIKELY(!string_scan(state))) { + raise_parse_error("unexpected end of input, expected closing \"", state); + } + + if (RB_LIKELY(*state->cursor == '"')) { + VALUE string = json_string_fastpath(state, config, start, state->cursor, is_name); + state->cursor++; + return json_push_value(state, config, string); } + return json_parse_escaped_string(state, config, is_name, start); +} + +#if JSON_CPU_LITTLE_ENDIAN_64BITS +// From: https://lemire.me/blog/2022/01/21/swar-explained-parsing-eight-digits/ +// Additional References: +// https://johnnylee-sde.github.io/Fast-numeric-string-to-int/ +// http://0x80.pl/notesen/2014-10-12-parsing-decimal-numbers-part-1-swar.html +static inline uint64_t decode_8digits_unrolled(uint64_t val) { + const uint64_t mask = 0x000000FF000000FF; + const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) + const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) + val -= 0x3030303030303030; + val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; + val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; + return val; +} + +static inline uint64_t decode_4digits_unrolled(uint32_t val) { + const uint32_t mask = 0x000000FF; + const uint32_t mul1 = 100; + val -= 0x30303030; + val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; + val = ((val & mask) * mul1) + (((val >> 16) & mask)); + return val; } +#endif +static inline int json_parse_digits(JSON_ParserState *state, uint64_t *accumulator) +{ + const char *start = state->cursor; + +#if JSON_CPU_LITTLE_ENDIAN_64BITS + while (rest(state) >= sizeof(uint64_t)) { + uint64_t next_8bytes; + memcpy(&next_8bytes, state->cursor, sizeof(uint64_t)); + // From: https://github.com/simdjson/simdjson/blob/32b301893c13d058095a07d9868edaaa42ee07aa/include/simdjson/generic/numberparsing.h#L333 + // Branchless version of: http://0x80.pl/articles/swar-digits-validate.html + uint64_t match = (next_8bytes & 0xF0F0F0F0F0F0F0F0) | (((next_8bytes + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4); -#line 1184 "parser.c" -enum {JSON_array_start = 1}; -enum {JSON_array_first_final = 17}; -enum {JSON_array_error = 0}; + if (match == 0x3333333333333333) { // 8 consecutive digits + *accumulator = (*accumulator * 100000000) + decode_8digits_unrolled(next_8bytes); + state->cursor += 8; + continue; + } -enum {JSON_array_en_main = 1}; + uint32_t consecutive_digits = trailing_zeros64(match ^ 0x3333333333333333) / CHAR_BIT; + if (consecutive_digits >= 4) { + *accumulator = (*accumulator * 10000) + decode_4digits_unrolled((uint32_t)next_8bytes); + state->cursor += 4; + consecutive_digits -= 4; + } -#line 432 "parser.rl" + while (consecutive_digits) { + *accumulator = *accumulator * 10 + (*state->cursor - '0'); + consecutive_digits--; + state->cursor++; + } + return (int)(state->cursor - start); + } +#endif -static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) + char next_char; + while (rb_isdigit(next_char = peek(state))) { + *accumulator = *accumulator * 10 + (next_char - '0'); + state->cursor++; + } + return (int)(state->cursor - start); +} + +static inline VALUE json_parse_number(JSON_ParserState *state, JSON_ParserConfig *config, bool negative, const char *start) { - int cs = EVIL; - VALUE array_class = json->array_class; + bool integer = true; + const char first_digit = *state->cursor; + + // Variables for Ryu optimization - extract digits during parsing + int32_t exponent = 0; + int decimal_point_pos = -1; + uint64_t mantissa = 0; + + // Parse integer part and extract mantissa digits + int mantissa_digits = json_parse_digits(state, &mantissa); - if (json->max_nesting && current_nesting > json->max_nesting) { - rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); + if (RB_UNLIKELY((first_digit == '0' && mantissa_digits > 1) || (negative && mantissa_digits == 0))) { + raise_parse_error_at("invalid number: %s", state, start); } - *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class); - - -#line 1206 "parser.c" - { - cs = JSON_array_start; - } - -#line 445 "parser.rl" - -#line 1213 "parser.c" - { - if ( p == pe ) - goto _test_eof; - switch ( cs ) - { -case 1: - if ( (*p) == 91 ) - goto st2; - goto st0; -st0: -cs = 0; - goto _out; -st2: - if ( ++p == pe ) - goto _test_eof2; -case 2: - switch( (*p) ) { - case 13: goto st2; - case 32: goto st2; - case 34: goto tr2; - case 45: goto tr2; - case 47: goto st13; - case 73: goto tr2; - case 78: goto tr2; - case 91: goto tr2; - case 93: goto tr4; - case 102: goto tr2; - case 110: goto tr2; - case 116: goto tr2; - case 123: goto tr2; - } - if ( (*p) > 10 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto tr2; - } else if ( (*p) >= 9 ) - goto st2; - goto st0; -tr2: -#line 409 "parser.rl" - { - VALUE v = Qnil; - char *np = JSON_parse_value(json, p, pe, &v, current_nesting); - if (np == NULL) { - p--; {p++; cs = 3; goto _out;} - } else { - if (NIL_P(json->array_class)) { - rb_ary_push(*result, v); - } else { - rb_funcall(*result, i_leftshift, 1, v); - } - {p = (( np))-1;} + + // Parse fractional part + if (peek(state) == '.') { + integer = false; + decimal_point_pos = mantissa_digits; // Remember position of decimal point + state->cursor++; + + int fractional_digits = json_parse_digits(state, &mantissa); + mantissa_digits += fractional_digits; + + if (RB_UNLIKELY(!fractional_digits)) { + raise_parse_error_at("invalid number: %s", state, start); } } - goto st3; -st3: - if ( ++p == pe ) - goto _test_eof3; -case 3: -#line 1272 "parser.c" - switch( (*p) ) { - case 13: goto st3; - case 32: goto st3; - case 44: goto st4; - case 47: goto st9; - case 93: goto tr4; - } - if ( 9 <= (*p) && (*p) <= 10 ) - goto st3; - goto st0; -st4: - if ( ++p == pe ) - goto _test_eof4; -case 4: - switch( (*p) ) { - case 13: goto st4; - case 32: goto st4; - case 34: goto tr2; - case 45: goto tr2; - case 47: goto st5; - case 73: goto tr2; - case 78: goto tr2; - case 91: goto tr2; - case 102: goto tr2; - case 110: goto tr2; - case 116: goto tr2; - case 123: goto tr2; - } - if ( (*p) > 10 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto tr2; - } else if ( (*p) >= 9 ) - goto st4; - goto st0; -st5: - if ( ++p == pe ) - goto _test_eof5; -case 5: - switch( (*p) ) { - case 42: goto st6; - case 47: goto st8; - } - goto st0; -st6: - if ( ++p == pe ) - goto _test_eof6; -case 6: - if ( (*p) == 42 ) - goto st7; - goto st6; -st7: - if ( ++p == pe ) - goto _test_eof7; -case 7: - switch( (*p) ) { - case 42: goto st7; - case 47: goto st4; - } - goto st6; -st8: - if ( ++p == pe ) - goto _test_eof8; -case 8: - if ( (*p) == 10 ) - goto st4; - goto st8; -st9: - if ( ++p == pe ) - goto _test_eof9; -case 9: - switch( (*p) ) { - case 42: goto st10; - case 47: goto st12; - } - goto st0; -st10: - if ( ++p == pe ) - goto _test_eof10; -case 10: - if ( (*p) == 42 ) - goto st11; - goto st10; -st11: - if ( ++p == pe ) - goto _test_eof11; -case 11: - switch( (*p) ) { - case 42: goto st11; - case 47: goto st3; - } - goto st10; -st12: - if ( ++p == pe ) - goto _test_eof12; -case 12: - if ( (*p) == 10 ) - goto st3; - goto st12; -tr4: -#line 424 "parser.rl" - { p--; {p++; cs = 17; goto _out;} } - goto st17; -st17: - if ( ++p == pe ) - goto _test_eof17; -case 17: -#line 1379 "parser.c" - goto st0; -st13: - if ( ++p == pe ) - goto _test_eof13; -case 13: - switch( (*p) ) { - case 42: goto st14; - case 47: goto st16; - } - goto st0; -st14: - if ( ++p == pe ) - goto _test_eof14; -case 14: - if ( (*p) == 42 ) - goto st15; - goto st14; -st15: - if ( ++p == pe ) - goto _test_eof15; -case 15: - switch( (*p) ) { - case 42: goto st15; - case 47: goto st2; - } - goto st14; -st16: - if ( ++p == pe ) - goto _test_eof16; -case 16: - if ( (*p) == 10 ) - goto st2; - goto st16; - } - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof11: cs = 11; goto _test_eof; - _test_eof12: cs = 12; goto _test_eof; - _test_eof17: cs = 17; goto _test_eof; - _test_eof13: cs = 13; goto _test_eof; - _test_eof14: cs = 14; goto _test_eof; - _test_eof15: cs = 15; goto _test_eof; - _test_eof16: cs = 16; goto _test_eof; - - _test_eof: {} - _out: {} - } - -#line 446 "parser.rl" - - if(cs >= JSON_array_first_final) { - return p + 1; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); - return NULL; + + // Parse exponent + if (rb_tolower(peek(state)) == 'e') { + integer = false; + state->cursor++; + + bool negative_exponent = false; + const char next_char = peek(state); + if (next_char == '-' || next_char == '+') { + negative_exponent = next_char == '-'; + state->cursor++; + } + + uint64_t abs_exponent = 0; + int exponent_digits = json_parse_digits(state, &abs_exponent); + + if (RB_UNLIKELY(!exponent_digits)) { + raise_parse_error_at("invalid number: %s", state, start); + } + + exponent = negative_exponent ? -((int32_t)abs_exponent) : ((int32_t)abs_exponent); + } + + if (integer) { + return json_decode_integer(mantissa, mantissa_digits, negative, start, state->cursor); + } + + // Adjust exponent based on decimal point position + if (decimal_point_pos >= 0) { + exponent -= (mantissa_digits - decimal_point_pos); } + + return json_decode_float(config, mantissa, mantissa_digits, exponent, negative, start, state->cursor); } -static const size_t MAX_STACK_BUFFER_SIZE = 128; -static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int symbolize) +static inline VALUE json_parse_positive_number(JSON_ParserState *state, JSON_ParserConfig *config) { - VALUE result = Qnil; - size_t bufferSize = stringEnd - string; - char *p = string, *pe = string, *unescape, *bufferStart, *buffer; - int unescape_len; - char buf[4]; + return json_parse_number(state, config, false, state->cursor); +} - if (bufferSize > MAX_STACK_BUFFER_SIZE) { -# ifdef HAVE_RB_ENC_INTERNED_STR - bufferStart = buffer = ALLOC_N(char, bufferSize ? bufferSize : 1); -# else - bufferStart = buffer = ALLOC_N(char, bufferSize); -# endif - } else { -# ifdef HAVE_RB_ENC_INTERNED_STR - bufferStart = buffer = ALLOCA_N(char, bufferSize ? bufferSize : 1); -# else - bufferStart = buffer = ALLOCA_N(char, bufferSize); -# endif - } +static inline VALUE json_parse_negative_number(JSON_ParserState *state, JSON_ParserConfig *config) +{ + const char *start = state->cursor; + state->cursor++; + return json_parse_number(state, config, true, start); +} - while (pe < stringEnd) { - if (*pe == '\\') { - unescape = (char *) "?"; - unescape_len = 1; - if (pe > p) { - MEMCPY(buffer, p, char, pe - p); - buffer += pe - p; +static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config) +{ + json_eat_whitespace(state); + + switch (peek(state)) { + case 'n': + if (rest(state) >= 4 && (memcmp(state->cursor, "null", 4) == 0)) { + state->cursor += 4; + return json_push_value(state, config, Qnil); } - switch (*++pe) { - case 'n': - unescape = (char *) "\n"; - break; - case 'r': - unescape = (char *) "\r"; - break; - case 't': - unescape = (char *) "\t"; - break; - case '"': - unescape = (char *) "\""; - break; - case '\\': - unescape = (char *) "\\"; - break; - case 'b': - unescape = (char *) "\b"; - break; - case 'f': - unescape = (char *) "\f"; - break; - case 'u': - if (pe > stringEnd - 4) { - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - ruby_xfree(bufferStart); - } - rb_enc_raise( - EXC_ENCODING eParserError, - "incomplete unicode character escape sequence at '%s'", p - ); - } else { - UTF32 ch = unescape_unicode((unsigned char *) ++pe); - pe += 3; - if (UNI_SUR_HIGH_START == (ch & 0xFC00)) { - pe++; - if (pe > stringEnd - 6) { - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - ruby_xfree(bufferStart); - } - rb_enc_raise( - EXC_ENCODING eParserError, - "incomplete surrogate pair at '%s'", p - ); - } - if (pe[0] == '\\' && pe[1] == 'u') { - UTF32 sur = unescape_unicode((unsigned char *) pe + 2); - ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) - | (sur & 0x3FF)); - pe += 5; - } else { - unescape = (char *) "?"; - break; - } + + raise_parse_error("unexpected token %s", state); + break; + case 't': + if (rest(state) >= 4 && (memcmp(state->cursor, "true", 4) == 0)) { + state->cursor += 4; + return json_push_value(state, config, Qtrue); + } + + raise_parse_error("unexpected token %s", state); + break; + case 'f': + // Note: memcmp with a small power of two compile to an integer comparison + if (rest(state) >= 5 && (memcmp(state->cursor + 1, "alse", 4) == 0)) { + state->cursor += 5; + return json_push_value(state, config, Qfalse); + } + + raise_parse_error("unexpected token %s", state); + break; + case 'N': + // Note: memcmp with a small power of two compile to an integer comparison + if (config->allow_nan && rest(state) >= 3 && (memcmp(state->cursor + 1, "aN", 2) == 0)) { + state->cursor += 3; + return json_push_value(state, config, CNaN); + } + + raise_parse_error("unexpected token %s", state); + break; + case 'I': + if (config->allow_nan && rest(state) >= 8 && (memcmp(state->cursor, "Infinity", 8) == 0)) { + state->cursor += 8; + return json_push_value(state, config, CInfinity); + } + + raise_parse_error("unexpected token %s", state); + break; + case '-': { + // Note: memcmp with a small power of two compile to an integer comparison + if (rest(state) >= 9 && (memcmp(state->cursor + 1, "Infinity", 8) == 0)) { + if (config->allow_nan) { + state->cursor += 9; + return json_push_value(state, config, CMinusInfinity); + } else { + raise_parse_error("unexpected token %s", state); + } + } + return json_push_value(state, config, json_parse_negative_number(state, config)); + break; + } + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + return json_push_value(state, config, json_parse_positive_number(state, config)); + break; + case '"': { + // %r{\A"[^"\\\t\n\x00]*(?:\\[bfnrtu\\/"][^"\\]*)*"} + return json_parse_string(state, config, false); + break; + } + case '[': { + state->cursor++; + json_eat_whitespace(state); + long stack_head = state->stack->head; + + if (peek(state) == ']') { + state->cursor++; + return json_push_value(state, config, json_decode_array(state, config, 0)); + } else { + state->current_nesting++; + if (RB_UNLIKELY(config->max_nesting && (config->max_nesting < state->current_nesting))) { + rb_raise(eNestingError, "nesting of %d is too deep", state->current_nesting); + } + state->in_array++; + json_parse_any(state, config); + } + + while (true) { + json_eat_whitespace(state); + + const char next_char = peek(state); + + if (RB_LIKELY(next_char == ',')) { + state->cursor++; + if (config->allow_trailing_comma) { + json_eat_whitespace(state); + if (peek(state) == ']') { + continue; } - unescape_len = convert_UTF32_to_UTF8(buf, ch); - unescape = buf; } - break; - default: - p = pe; + json_parse_any(state, config); continue; + } + + if (next_char == ']') { + state->cursor++; + long count = state->stack->head - stack_head; + state->current_nesting--; + state->in_array--; + return json_push_value(state, config, json_decode_array(state, config, count)); + } + + raise_parse_error("expected ',' or ']' after array value", state); } - MEMCPY(buffer, unescape, char, unescape_len); - buffer += unescape_len; - p = ++pe; - } else { - pe++; + break; } - } + case '{': { + const char *object_start_cursor = state->cursor; - if (pe > p) { - MEMCPY(buffer, p, char, pe - p); - buffer += pe - p; - } + state->cursor++; + json_eat_whitespace(state); + long stack_head = state->stack->head; -# ifdef HAVE_RB_ENC_INTERNED_STR - if (intern) { - result = rb_enc_interned_str(bufferStart, (long)(buffer - bufferStart), rb_utf8_encoding()); - } else { - result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); - } - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - ruby_xfree(bufferStart); - } -# else - result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); + if (peek(state) == '}') { + state->cursor++; + return json_push_value(state, config, json_decode_object(state, config, 0)); + } else { + state->current_nesting++; + if (RB_UNLIKELY(config->max_nesting && (config->max_nesting < state->current_nesting))) { + rb_raise(eNestingError, "nesting of %d is too deep", state->current_nesting); + } - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - ruby_xfree(bufferStart); - } + if (peek(state) != '"') { + raise_parse_error("expected object key, got %s", state); + } + json_parse_string(state, config, true); - if (intern) { - # if STR_UMINUS_DEDUPE_FROZEN - // Starting from MRI 2.8 it is preferable to freeze the string - // before deduplication so that it can be interned directly - // otherwise it would be duplicated first which is wasteful. - result = rb_funcall(rb_str_freeze(result), i_uminus, 0); - # elif STR_UMINUS_DEDUPE - // MRI 2.5 and older do not deduplicate strings that are already - // frozen. - result = rb_funcall(result, i_uminus, 0); - # else - result = rb_str_freeze(result); - # endif - } -# endif + json_eat_whitespace(state); + if (peek(state) != ':') { + raise_parse_error("expected ':' after object key", state); + } + state->cursor++; - if (symbolize) { - result = rb_str_intern(result); - } + json_parse_any(state, config); + } - return result; -} + while (true) { + json_eat_whitespace(state); + const char next_char = peek(state); + if (next_char == '}') { + state->cursor++; + state->current_nesting--; + size_t count = state->stack->head - stack_head; -#line 1592 "parser.c" -enum {JSON_string_start = 1}; -enum {JSON_string_first_final = 8}; -enum {JSON_string_error = 0}; + // Temporary rewind cursor in case an error is raised + const char *final_cursor = state->cursor; + state->cursor = object_start_cursor; + VALUE object = json_decode_object(state, config, count); + state->cursor = final_cursor; -enum {JSON_string_en_main = 1}; + return json_push_value(state, config, object); + } + if (next_char == ',') { + state->cursor++; + json_eat_whitespace(state); -#line 620 "parser.rl" + if (config->allow_trailing_comma) { + if (peek(state) == '}') { + continue; + } + } + if (RB_UNLIKELY(peek(state) != '"')) { + raise_parse_error("expected object key, got: %s", state); + } + json_parse_string(state, config, true); -static int -match_i(VALUE regexp, VALUE klass, VALUE memo) -{ - if (regexp == Qundef) return ST_STOP; - if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) && - RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) { - rb_ary_push(memo, klass); - return ST_STOP; - } - return ST_CONTINUE; -} + json_eat_whitespace(state); + if (RB_UNLIKELY(peek(state) != ':')) { + raise_parse_error("expected ':' after object key, got: %s", state); + } + state->cursor++; -static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result) -{ - int cs = EVIL; - VALUE match_string; - - -#line 1621 "parser.c" - { - cs = JSON_string_start; - } - -#line 640 "parser.rl" - json->memo = p; - -#line 1629 "parser.c" - { - if ( p == pe ) - goto _test_eof; - switch ( cs ) - { -case 1: - if ( (*p) == 34 ) - goto st2; - goto st0; -st0: -cs = 0; - goto _out; -st2: - if ( ++p == pe ) - goto _test_eof2; -case 2: - switch( (*p) ) { - case 34: goto tr2; - case 92: goto st3; - } - if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 ) - goto st0; - goto st2; -tr2: -#line 607 "parser.rl" - { - *result = json_string_unescape(json->memo + 1, p, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names); - if (NIL_P(*result)) { - p--; - {p++; cs = 8; goto _out;} - } else { - {p = (( p + 1))-1;} + json_parse_any(state, config); + + continue; + } + + raise_parse_error("expected ',' or '}' after object value, got: %s", state); + } + break; } - } -#line 617 "parser.rl" - { p--; {p++; cs = 8; goto _out;} } - goto st8; -st8: - if ( ++p == pe ) - goto _test_eof8; -case 8: -#line 1671 "parser.c" - goto st0; -st3: - if ( ++p == pe ) - goto _test_eof3; -case 3: - if ( (*p) == 117 ) - goto st4; - if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 ) - goto st0; - goto st2; -st4: - if ( ++p == pe ) - goto _test_eof4; -case 4: - if ( (*p) < 65 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto st5; - } else if ( (*p) > 70 ) { - if ( 97 <= (*p) && (*p) <= 102 ) - goto st5; - } else - goto st5; - goto st0; -st5: - if ( ++p == pe ) - goto _test_eof5; -case 5: - if ( (*p) < 65 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto st6; - } else if ( (*p) > 70 ) { - if ( 97 <= (*p) && (*p) <= 102 ) - goto st6; - } else - goto st6; - goto st0; -st6: - if ( ++p == pe ) - goto _test_eof6; -case 6: - if ( (*p) < 65 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto st7; - } else if ( (*p) > 70 ) { - if ( 97 <= (*p) && (*p) <= 102 ) - goto st7; - } else - goto st7; - goto st0; -st7: - if ( ++p == pe ) - goto _test_eof7; -case 7: - if ( (*p) < 65 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto st2; - } else if ( (*p) > 70 ) { - if ( 97 <= (*p) && (*p) <= 102 ) - goto st2; - } else - goto st2; - goto st0; - } - _test_eof2: cs = 2; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - - _test_eof: {} - _out: {} - } - -#line 642 "parser.rl" - - if (json->create_additions && RTEST(match_string = json->match_string)) { - VALUE klass; - VALUE memo = rb_ary_new2(2); - rb_ary_push(memo, *result); - rb_hash_foreach(match_string, match_i, memo); - klass = rb_ary_entry(memo, 1); - if (RTEST(klass)) { - *result = rb_funcall(klass, i_json_create, 1, *result); - } + + case 0: + raise_parse_error("unexpected end of input", state); + break; + + default: + raise_parse_error("unexpected character: %s", state); + break; } - if (cs >= JSON_string_first_final) { - return p + 1; - } else { - return NULL; + raise_parse_error("unreachable: %s", state); + return Qundef; +} + +static void json_ensure_eof(JSON_ParserState *state) +{ + json_eat_whitespace(state); + if (!eos(state)) { + raise_parse_error("unexpected token at end of stream %s", state); } } @@ -1777,24 +1411,84 @@ case 7: static VALUE convert_encoding(VALUE source) { -#ifdef HAVE_RUBY_ENCODING_H - rb_encoding *enc = rb_enc_get(source); - if (enc == rb_ascii8bit_encoding()) { - if (OBJ_FROZEN(source)) { - source = rb_str_dup(source); - } - FORCE_UTF8(source); - } else { - source = rb_str_conv_enc(source, rb_enc_get(source), rb_utf8_encoding()); - } -#endif + int encindex = RB_ENCODING_GET(source); + + if (RB_LIKELY(encindex == utf8_encindex)) { return source; + } + + if (encindex == binary_encindex) { + // For historical reason, we silently reinterpret binary strings as UTF-8 + return rb_enc_associate_index(rb_str_dup(source), utf8_encindex); + } + + return rb_funcall(source, i_encode, 1, Encoding_UTF_8); +} + +static int parser_config_init_i(VALUE key, VALUE val, VALUE data) +{ + JSON_ParserConfig *config = (JSON_ParserConfig *)data; + + if (key == sym_max_nesting) { config->max_nesting = RTEST(val) ? FIX2INT(val) : 0; } + else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); } + else if (key == sym_allow_trailing_comma) { config->allow_trailing_comma = RTEST(val); } + else if (key == sym_allow_control_characters) { config->allow_control_characters = RTEST(val); } + else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); } + else if (key == sym_freeze) { config->freeze = RTEST(val); } + else if (key == sym_on_load) { config->on_load_proc = RTEST(val) ? val : Qfalse; } + else if (key == sym_allow_duplicate_key) { config->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; } + else if (key == sym_decimal_class) { + if (RTEST(val)) { + if (rb_respond_to(val, i_try_convert)) { + config->decimal_class = val; + config->decimal_method_id = i_try_convert; + } else if (rb_respond_to(val, i_new)) { + config->decimal_class = val; + config->decimal_method_id = i_new; + } else if (RB_TYPE_P(val, T_CLASS)) { + VALUE name = rb_class_name(val); + const char *name_cstr = RSTRING_PTR(name); + const char *last_colon = strrchr(name_cstr, ':'); + if (last_colon) { + const char *mod_path_end = last_colon - 1; + VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr); + config->decimal_class = rb_path_to_class(mod_path); + + const char *method_name_beg = last_colon + 1; + long before_len = method_name_beg - name_cstr; + long len = RSTRING_LEN(name) - before_len; + VALUE method_name = rb_str_substr(name, before_len, len); + config->decimal_method_id = SYM2ID(rb_str_intern(method_name)); + } else { + config->decimal_class = rb_mKernel; + config->decimal_method_id = SYM2ID(rb_str_intern(name)); + } + } + } + } + + return ST_CONTINUE; +} + +static void parser_config_init(JSON_ParserConfig *config, VALUE opts) +{ + config->max_nesting = 100; + + if (!NIL_P(opts)) { + Check_Type(opts, T_HASH); + if (RHASH_SIZE(opts) > 0) { + // We assume in most cases few keys are set so it's faster to go over + // the provided keys than to check all possible keys. + rb_hash_foreach(opts, parser_config_init_i, (VALUE)config); + } + + } } /* - * call-seq: new(source, opts => {}) + * call-seq: new(opts => {}) * - * Creates a new JSON::Ext::Parser instance for the string _source_. + * Creates a new JSON::Ext::ParserConfig instance. * * It will be configured by the _opts_ hash. _opts_ can have the following * keys: @@ -1810,343 +1504,115 @@ static VALUE convert_encoding(VALUE source) * (keys) in a JSON object. Otherwise strings are returned, which is * also the default. It's not possible to use this option in * conjunction with the *create_additions* option. - * * *create_additions*: If set to false, the Parser doesn't create - * additions even if a matching class and create_id was found. This option - * defaults to false. - * * *object_class*: Defaults to Hash - * * *array_class*: Defaults to Array + * * *decimal_class*: Specifies which class to use instead of the default + * (Float) when parsing decimal numbers. This class must accept a single + * string argument in its constructor. */ -static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) +static VALUE cParserConfig_initialize(VALUE self, VALUE opts) { - VALUE source, opts; - GET_PARSER_INIT; + rb_check_frozen(self); + GET_PARSER_CONFIG; + + parser_config_init(config, opts); + + RB_OBJ_WRITTEN(self, Qundef, config->decimal_class); - if (json->Vsource) { - rb_raise(rb_eTypeError, "already initialized instance"); - } - rb_scan_args(argc, argv, "1:", &source, &opts); - if (!NIL_P(opts)) { - VALUE tmp = ID2SYM(i_max_nesting); - if (option_given_p(opts, tmp)) { - VALUE max_nesting = rb_hash_aref(opts, tmp); - if (RTEST(max_nesting)) { - Check_Type(max_nesting, T_FIXNUM); - json->max_nesting = FIX2INT(max_nesting); - } else { - json->max_nesting = 0; - } - } else { - json->max_nesting = 100; - } - tmp = ID2SYM(i_allow_nan); - if (option_given_p(opts, tmp)) { - json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; - } else { - json->allow_nan = 0; - } - tmp = ID2SYM(i_symbolize_names); - if (option_given_p(opts, tmp)) { - json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; - } else { - json->symbolize_names = 0; - } - tmp = ID2SYM(i_freeze); - if (option_given_p(opts, tmp)) { - json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; - } else { - json->freeze = 0; - } - tmp = ID2SYM(i_create_additions); - if (option_given_p(opts, tmp)) { - json->create_additions = RTEST(rb_hash_aref(opts, tmp)); - } else { - json->create_additions = 0; - } - if (json->symbolize_names && json->create_additions) { - rb_raise(rb_eArgError, - "options :symbolize_names and :create_additions cannot be " - " used in conjunction"); - } - tmp = ID2SYM(i_create_id); - if (option_given_p(opts, tmp)) { - json->create_id = rb_hash_aref(opts, tmp); - } else { - json->create_id = rb_funcall(mJSON, i_create_id, 0); - } - tmp = ID2SYM(i_object_class); - if (option_given_p(opts, tmp)) { - json->object_class = rb_hash_aref(opts, tmp); - } else { - json->object_class = Qnil; - } - tmp = ID2SYM(i_array_class); - if (option_given_p(opts, tmp)) { - json->array_class = rb_hash_aref(opts, tmp); - } else { - json->array_class = Qnil; - } - tmp = ID2SYM(i_decimal_class); - if (option_given_p(opts, tmp)) { - json->decimal_class = rb_hash_aref(opts, tmp); - } else { - json->decimal_class = Qnil; - } - tmp = ID2SYM(i_match_string); - if (option_given_p(opts, tmp)) { - VALUE match_string = rb_hash_aref(opts, tmp); - json->match_string = RTEST(match_string) ? match_string : Qnil; - } else { - json->match_string = Qnil; - } - } else { - json->max_nesting = 100; - json->allow_nan = 0; - json->create_additions = 0; - json->create_id = Qnil; - json->object_class = Qnil; - json->array_class = Qnil; - json->decimal_class = Qnil; - } - source = convert_encoding(StringValue(source)); - StringValue(source); - json->len = RSTRING_LEN(source); - json->source = RSTRING_PTR(source);; - json->Vsource = source; return self; } +static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource) +{ + Vsource = convert_encoding(StringValue(Vsource)); + StringValue(Vsource); + + VALUE rvalue_stack_buffer[RVALUE_STACK_INITIAL_CAPA]; + rvalue_stack stack = { + .type = RVALUE_STACK_STACK_ALLOCATED, + .ptr = rvalue_stack_buffer, + .capa = RVALUE_STACK_INITIAL_CAPA, + }; -#line 1920 "parser.c" -enum {JSON_start = 1}; -enum {JSON_first_final = 10}; -enum {JSON_error = 0}; + long len; + const char *start; + RSTRING_GETMEM(Vsource, start, len); -enum {JSON_en_main = 1}; + JSON_ParserState _state = { + .start = start, + .cursor = start, + .end = start + len, + .stack = &stack, + }; + JSON_ParserState *state = &_state; + VALUE result = json_parse_any(state, config); -#line 828 "parser.rl" + // This may be skipped in case of exception, but + // it won't cause a leak. + rvalue_stack_eagerly_release(state->stack_handle); + json_ensure_eof(state); + + return result; +} /* - * call-seq: parse() + * call-seq: parse(source) * * Parses the current JSON text _source_ and returns the complete data * structure as a result. * It raises JSON::ParserError if fail to parse. */ -static VALUE cParser_parse(VALUE self) +static VALUE cParserConfig_parse(VALUE self, VALUE Vsource) { - char *p, *pe; - int cs = EVIL; - VALUE result = Qnil; - GET_PARSER; - - -#line 1946 "parser.c" - { - cs = JSON_start; - } - -#line 845 "parser.rl" - p = json->source; - pe = p + json->len; - -#line 1955 "parser.c" - { - if ( p == pe ) - goto _test_eof; - switch ( cs ) - { -st1: - if ( ++p == pe ) - goto _test_eof1; -case 1: - switch( (*p) ) { - case 13: goto st1; - case 32: goto st1; - case 34: goto tr2; - case 45: goto tr2; - case 47: goto st6; - case 73: goto tr2; - case 78: goto tr2; - case 91: goto tr2; - case 102: goto tr2; - case 110: goto tr2; - case 116: goto tr2; - case 123: goto tr2; - } - if ( (*p) > 10 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto tr2; - } else if ( (*p) >= 9 ) - goto st1; - goto st0; -st0: -cs = 0; - goto _out; -tr2: -#line 820 "parser.rl" - { - char *np = JSON_parse_value(json, p, pe, &result, 0); - if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;} - } - goto st10; -st10: - if ( ++p == pe ) - goto _test_eof10; -case 10: -#line 1999 "parser.c" - switch( (*p) ) { - case 13: goto st10; - case 32: goto st10; - case 47: goto st2; - } - if ( 9 <= (*p) && (*p) <= 10 ) - goto st10; - goto st0; -st2: - if ( ++p == pe ) - goto _test_eof2; -case 2: - switch( (*p) ) { - case 42: goto st3; - case 47: goto st5; - } - goto st0; -st3: - if ( ++p == pe ) - goto _test_eof3; -case 3: - if ( (*p) == 42 ) - goto st4; - goto st3; -st4: - if ( ++p == pe ) - goto _test_eof4; -case 4: - switch( (*p) ) { - case 42: goto st4; - case 47: goto st10; - } - goto st3; -st5: - if ( ++p == pe ) - goto _test_eof5; -case 5: - if ( (*p) == 10 ) - goto st10; - goto st5; -st6: - if ( ++p == pe ) - goto _test_eof6; -case 6: - switch( (*p) ) { - case 42: goto st7; - case 47: goto st9; - } - goto st0; -st7: - if ( ++p == pe ) - goto _test_eof7; -case 7: - if ( (*p) == 42 ) - goto st8; - goto st7; -st8: - if ( ++p == pe ) - goto _test_eof8; -case 8: - switch( (*p) ) { - case 42: goto st8; - case 47: goto st1; - } - goto st7; -st9: - if ( ++p == pe ) - goto _test_eof9; -case 9: - if ( (*p) == 10 ) - goto st1; - goto st9; - } - _test_eof1: cs = 1; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - - _test_eof: {} - _out: {} - } - -#line 848 "parser.rl" - - if (cs >= JSON_first_final && p == pe) { - return result; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); - return Qnil; - } + GET_PARSER_CONFIG; + return cParser_parse(config, Vsource); } -static void JSON_mark(void *ptr) +static VALUE cParser_m_parse(VALUE klass, VALUE Vsource, VALUE opts) { - JSON_Parser *json = ptr; - rb_gc_mark_maybe(json->Vsource); - rb_gc_mark_maybe(json->create_id); - rb_gc_mark_maybe(json->object_class); - rb_gc_mark_maybe(json->array_class); - rb_gc_mark_maybe(json->decimal_class); - rb_gc_mark_maybe(json->match_string); + Vsource = convert_encoding(StringValue(Vsource)); + StringValue(Vsource); + + JSON_ParserConfig _config = {0}; + JSON_ParserConfig *config = &_config; + parser_config_init(config, opts); + + return cParser_parse(config, Vsource); +} + +static void JSON_ParserConfig_mark(void *ptr) +{ + JSON_ParserConfig *config = ptr; + rb_gc_mark(config->on_load_proc); + rb_gc_mark(config->decimal_class); } -static void JSON_free(void *ptr) +static void JSON_ParserConfig_free(void *ptr) { - JSON_Parser *json = ptr; - fbuffer_free(json->fbuffer); - ruby_xfree(json); + JSON_ParserConfig *config = ptr; + ruby_xfree(config); } -static size_t JSON_memsize(const void *ptr) +static size_t JSON_ParserConfig_memsize(const void *ptr) { - const JSON_Parser *json = ptr; - return sizeof(*json) + FBUFFER_CAPA(json->fbuffer); + return sizeof(JSON_ParserConfig); } -#ifdef NEW_TYPEDDATA_WRAPPER -static const rb_data_type_t JSON_Parser_type = { - "JSON/Parser", - {JSON_mark, JSON_free, JSON_memsize,}, -#ifdef RUBY_TYPED_FREE_IMMEDIATELY +static const rb_data_type_t JSON_ParserConfig_type = { + "JSON::Ext::Parser/ParserConfig", + { + JSON_ParserConfig_mark, + JSON_ParserConfig_free, + JSON_ParserConfig_memsize, + }, 0, 0, - RUBY_TYPED_FREE_IMMEDIATELY, -#endif + RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE, }; -#endif static VALUE cJSON_parser_s_allocate(VALUE klass) { - JSON_Parser *json; - VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json); - json->fbuffer = fbuffer_alloc(0); - return obj; -} - -/* - * call-seq: source() - * - * Returns a copy of the current _source_ string, that was used to construct - * this Parser. - */ -static VALUE cParser_source(VALUE self) -{ - GET_PARSER; - return rb_str_dup(json->Vsource); + JSON_ParserConfig *config; + return TypedData_Make_Struct(klass, JSON_ParserConfig, &JSON_ParserConfig_type, config); } void Init_parser(void) @@ -2158,16 +1624,16 @@ void Init_parser(void) #undef rb_intern rb_require("json/common"); mJSON = rb_define_module("JSON"); - mExt = rb_define_module_under(mJSON, "Ext"); - cParser = rb_define_class_under(mExt, "Parser", rb_cObject); - eParserError = rb_path2class("JSON::ParserError"); + VALUE mExt = rb_define_module_under(mJSON, "Ext"); + VALUE cParserConfig = rb_define_class_under(mExt, "ParserConfig", rb_cObject); eNestingError = rb_path2class("JSON::NestingError"); - rb_gc_register_mark_object(eParserError); rb_gc_register_mark_object(eNestingError); - rb_define_alloc_func(cParser, cJSON_parser_s_allocate); - rb_define_method(cParser, "initialize", cParser_initialize, -1); - rb_define_method(cParser, "parse", cParser_parse, 0); - rb_define_method(cParser, "source", cParser_source, 0); + rb_define_alloc_func(cParserConfig, cJSON_parser_s_allocate); + rb_define_method(cParserConfig, "initialize", cParserConfig_initialize, 1); + rb_define_method(cParserConfig, "parse", cParserConfig_parse, 1); + + VALUE cParser = rb_define_class_under(mExt, "Parser", rb_cObject); + rb_define_singleton_method(cParser, "parse", cParser_m_parse, 2); CNaN = rb_const_get(mJSON, rb_intern("NaN")); rb_gc_register_mark_object(CNaN); @@ -2178,34 +1644,29 @@ void Init_parser(void) CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity")); rb_gc_register_mark_object(CMinusInfinity); - i_json_creatable_p = rb_intern("json_creatable?"); - i_json_create = rb_intern("json_create"); - i_create_id = rb_intern("create_id"); - i_create_additions = rb_intern("create_additions"); - i_chr = rb_intern("chr"); - i_max_nesting = rb_intern("max_nesting"); - i_allow_nan = rb_intern("allow_nan"); - i_symbolize_names = rb_intern("symbolize_names"); - i_object_class = rb_intern("object_class"); - i_array_class = rb_intern("array_class"); - i_decimal_class = rb_intern("decimal_class"); - i_match = rb_intern("match"); - i_match_string = rb_intern("match_string"); - i_key_p = rb_intern("key?"); - i_deep_const_get = rb_intern("deep_const_get"); - i_aset = rb_intern("[]="); - i_aref = rb_intern("[]"); - i_leftshift = rb_intern("<<"); + rb_global_variable(&Encoding_UTF_8); + Encoding_UTF_8 = rb_const_get(rb_path2class("Encoding"), rb_intern("UTF_8")); + + sym_max_nesting = ID2SYM(rb_intern("max_nesting")); + sym_allow_nan = ID2SYM(rb_intern("allow_nan")); + sym_allow_trailing_comma = ID2SYM(rb_intern("allow_trailing_comma")); + sym_allow_control_characters = ID2SYM(rb_intern("allow_control_characters")); + sym_symbolize_names = ID2SYM(rb_intern("symbolize_names")); + sym_freeze = ID2SYM(rb_intern("freeze")); + sym_on_load = ID2SYM(rb_intern("on_load")); + sym_decimal_class = ID2SYM(rb_intern("decimal_class")); + sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key")); + i_new = rb_intern("new"); i_try_convert = rb_intern("try_convert"); - i_freeze = rb_intern("freeze"); i_uminus = rb_intern("-@"); -} + i_encode = rb_intern("encode"); -/* - * Local variables: - * mode: c - * c-file-style: ruby - * indent-tabs-mode: nil - * End: - */ + binary_encindex = rb_ascii8bit_encindex(); + utf8_encindex = rb_utf8_encindex(); + enc_utf8 = rb_utf8_encoding(); + +#ifdef HAVE_SIMD + simd_impl = find_simd_implementation(); +#endif +} diff --git a/ext/json/parser/parser.h b/ext/json/parser/parser.h deleted file mode 100644 index 92ed3fdc5d..0000000000 --- a/ext/json/parser/parser.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef _PARSER_H_ -#define _PARSER_H_ - -#include "ruby.h" - -#ifndef HAVE_RUBY_RE_H -#include "re.h" -#endif - -#ifdef HAVE_RUBY_ST_H -#include "ruby/st.h" -#else -#include "st.h" -#endif - -#ifndef MAYBE_UNUSED -# define MAYBE_UNUSED(x) x -#endif - -#define option_given_p(opts, key) RTEST(rb_funcall(opts, i_key_p, 1, key)) - -/* unicode */ - -typedef unsigned long UTF32; /* at least 32 bits */ -typedef unsigned short UTF16; /* at least 16 bits */ -typedef unsigned char UTF8; /* typically 8 bits */ - -#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD -#define UNI_SUR_HIGH_START (UTF32)0xD800 -#define UNI_SUR_HIGH_END (UTF32)0xDBFF -#define UNI_SUR_LOW_START (UTF32)0xDC00 -#define UNI_SUR_LOW_END (UTF32)0xDFFF - -typedef struct JSON_ParserStruct { - VALUE Vsource; - char *source; - long len; - char *memo; - VALUE create_id; - int max_nesting; - int allow_nan; - int parsing_name; - int symbolize_names; - int freeze; - VALUE object_class; - VALUE array_class; - VALUE decimal_class; - int create_additions; - VALUE match_string; - FBuffer *fbuffer; -} JSON_Parser; - -#define GET_PARSER \ - GET_PARSER_INIT; \ - if (!json->Vsource) rb_raise(rb_eTypeError, "uninitialized instance") -#define GET_PARSER_INIT \ - JSON_Parser *json; \ - TypedData_Get_Struct(self, JSON_Parser, &JSON_Parser_type, json) - -#define MinusInfinity "-Infinity" -#define EVIL 0x666 - -static UTF32 unescape_unicode(const unsigned char *p); -static int convert_UTF32_to_UTF8(char *buf, UTF32 ch); -static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); -static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); -static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result); -static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result); -static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); -static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int symbolize); -static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result); -static VALUE convert_encoding(VALUE source); -static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self); -static VALUE cParser_parse(VALUE self); -static void JSON_mark(void *json); -static void JSON_free(void *json); -static VALUE cJSON_parser_s_allocate(VALUE klass); -static VALUE cParser_source(VALUE self); -#ifndef ZALLOC -#define ZALLOC(type) ((type *)ruby_zalloc(sizeof(type))) -static inline void *ruby_zalloc(size_t n) -{ - void *p = ruby_xmalloc(n); - memset(p, 0, n); - return p; -} -#endif -#ifdef TypedData_Make_Struct -static const rb_data_type_t JSON_Parser_type; -#define NEW_TYPEDDATA_WRAPPER 1 -#else -#define TypedData_Make_Struct(klass, type, ignore, json) Data_Make_Struct(klass, type, NULL, JSON_free, json) -#define TypedData_Get_Struct(self, JSON_Parser, ignore, json) Data_Get_Struct(self, JSON_Parser, json) -#endif - -#endif diff --git a/ext/json/parser/parser.rl b/ext/json/parser/parser.rl deleted file mode 100644 index af190e7500..0000000000 --- a/ext/json/parser/parser.rl +++ /dev/null @@ -1,971 +0,0 @@ -#include "../fbuffer/fbuffer.h" -#include "parser.h" - -#if defined HAVE_RUBY_ENCODING_H -# define EXC_ENCODING rb_utf8_encoding(), -# ifndef HAVE_RB_ENC_RAISE -static void -enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...) -{ - va_list args; - VALUE mesg; - - va_start(args, fmt); - mesg = rb_enc_vsprintf(enc, fmt, args); - va_end(args); - - rb_exc_raise(rb_exc_new3(exc, mesg)); -} -# define rb_enc_raise enc_raise -# endif -#else -# define EXC_ENCODING /* nothing */ -# define rb_enc_raise rb_raise -#endif - -/* unicode */ - -static const signed char digit_values[256] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, - -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1 -}; - -static UTF32 unescape_unicode(const unsigned char *p) -{ - signed char b; - UTF32 result = 0; - b = digit_values[p[0]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - b = digit_values[p[1]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - b = digit_values[p[2]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - b = digit_values[p[3]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - return result; -} - -static int convert_UTF32_to_UTF8(char *buf, UTF32 ch) -{ - int len = 1; - if (ch <= 0x7F) { - buf[0] = (char) ch; - } else if (ch <= 0x07FF) { - buf[0] = (char) ((ch >> 6) | 0xC0); - buf[1] = (char) ((ch & 0x3F) | 0x80); - len++; - } else if (ch <= 0xFFFF) { - buf[0] = (char) ((ch >> 12) | 0xE0); - buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80); - buf[2] = (char) ((ch & 0x3F) | 0x80); - len += 2; - } else if (ch <= 0x1fffff) { - buf[0] =(char) ((ch >> 18) | 0xF0); - buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80); - buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80); - buf[3] =(char) ((ch & 0x3F) | 0x80); - len += 3; - } else { - buf[0] = '?'; - } - return len; -} - -static VALUE mJSON, mExt, cParser, eParserError, eNestingError; -static VALUE CNaN, CInfinity, CMinusInfinity; - -static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, - i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, - i_object_class, i_array_class, i_decimal_class, i_key_p, - i_deep_const_get, i_match, i_match_string, i_aset, i_aref, - i_leftshift, i_new, i_try_convert, i_freeze, i_uminus; - -%%{ - machine JSON_common; - - cr = '\n'; - cr_neg = [^\n]; - ws = [ \t\r\n]; - c_comment = '/*' ( any* - (any* '*/' any* ) ) '*/'; - cpp_comment = '//' cr_neg* cr; - comment = c_comment | cpp_comment; - ignore = ws | comment; - name_separator = ':'; - value_separator = ','; - Vnull = 'null'; - Vfalse = 'false'; - Vtrue = 'true'; - VNaN = 'NaN'; - VInfinity = 'Infinity'; - VMinusInfinity = '-Infinity'; - begin_value = [nft\"\-\[\{NI] | digit; - begin_object = '{'; - end_object = '}'; - begin_array = '['; - end_array = ']'; - begin_string = '"'; - begin_name = begin_string; - begin_number = digit | '-'; -}%% - -%%{ - machine JSON_object; - include JSON_common; - - write data; - - action parse_value { - VALUE v = Qnil; - char *np = JSON_parse_value(json, fpc, pe, &v, current_nesting); - if (np == NULL) { - fhold; fbreak; - } else { - if (NIL_P(json->object_class)) { - OBJ_FREEZE(last_name); - rb_hash_aset(*result, last_name, v); - } else { - rb_funcall(*result, i_aset, 2, last_name, v); - } - fexec np; - } - } - - action parse_name { - char *np; - json->parsing_name = 1; - np = JSON_parse_string(json, fpc, pe, &last_name); - json->parsing_name = 0; - if (np == NULL) { fhold; fbreak; } else fexec np; - } - - action exit { fhold; fbreak; } - - pair = ignore* begin_name >parse_name ignore* name_separator ignore* begin_value >parse_value; - next_pair = ignore* value_separator pair; - - main := ( - begin_object - (pair (next_pair)*)? ignore* - end_object - ) @exit; -}%% - -static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) -{ - int cs = EVIL; - VALUE last_name = Qnil; - VALUE object_class = json->object_class; - - if (json->max_nesting && current_nesting > json->max_nesting) { - rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); - } - - *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class); - - %% write init; - %% write exec; - - if (cs >= JSON_object_first_final) { - if (json->create_additions) { - VALUE klassname; - if (NIL_P(json->object_class)) { - klassname = rb_hash_aref(*result, json->create_id); - } else { - klassname = rb_funcall(*result, i_aref, 1, json->create_id); - } - if (!NIL_P(klassname)) { - VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname); - if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) { - *result = rb_funcall(klass, i_json_create, 1, *result); - } - } - } - return p + 1; - } else { - return NULL; - } -} - - -%%{ - machine JSON_value; - include JSON_common; - - write data; - - action parse_null { - *result = Qnil; - } - action parse_false { - *result = Qfalse; - } - action parse_true { - *result = Qtrue; - } - action parse_nan { - if (json->allow_nan) { - *result = CNaN; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 2); - } - } - action parse_infinity { - if (json->allow_nan) { - *result = CInfinity; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 7); - } - } - action parse_string { - char *np = JSON_parse_string(json, fpc, pe, result); - if (np == NULL) { fhold; fbreak; } else fexec np; - } - - action parse_number { - char *np; - if(pe > fpc + 8 && !strncmp(MinusInfinity, fpc, 9)) { - if (json->allow_nan) { - *result = CMinusInfinity; - fexec p + 10; - fhold; fbreak; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); - } - } - np = JSON_parse_float(json, fpc, pe, result); - if (np != NULL) fexec np; - np = JSON_parse_integer(json, fpc, pe, result); - if (np != NULL) fexec np; - fhold; fbreak; - } - - action parse_array { - char *np; - np = JSON_parse_array(json, fpc, pe, result, current_nesting + 1); - if (np == NULL) { fhold; fbreak; } else fexec np; - } - - action parse_object { - char *np; - np = JSON_parse_object(json, fpc, pe, result, current_nesting + 1); - if (np == NULL) { fhold; fbreak; } else fexec np; - } - - action exit { fhold; fbreak; } - -main := ignore* ( - Vnull @parse_null | - Vfalse @parse_false | - Vtrue @parse_true | - VNaN @parse_nan | - VInfinity @parse_infinity | - begin_number >parse_number | - begin_string >parse_string | - begin_array >parse_array | - begin_object >parse_object - ) ignore* %*exit; -}%% - -static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) -{ - int cs = EVIL; - - %% write init; - %% write exec; - - if (json->freeze) { - OBJ_FREEZE(*result); - } - - if (cs >= JSON_value_first_final) { - return p; - } else { - return NULL; - } -} - -%%{ - machine JSON_integer; - - write data; - - action exit { fhold; fbreak; } - - main := '-'? ('0' | [1-9][0-9]*) (^[0-9]? @exit); -}%% - -static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result) -{ - int cs = EVIL; - - %% write init; - json->memo = p; - %% write exec; - - if (cs >= JSON_integer_first_final) { - long len = p - json->memo; - fbuffer_clear(json->fbuffer); - fbuffer_append(json->fbuffer, json->memo, len); - fbuffer_append_char(json->fbuffer, '\0'); - *result = rb_cstr2inum(FBUFFER_PTR(json->fbuffer), 10); - return p + 1; - } else { - return NULL; - } -} - -%%{ - machine JSON_float; - include JSON_common; - - write data; - - action exit { fhold; fbreak; } - - main := '-'? ( - (('0' | [1-9][0-9]*) '.' [0-9]+ ([Ee] [+\-]?[0-9]+)?) - | (('0' | [1-9][0-9]*) ([Ee] [+\-]?[0-9]+)) - ) (^[0-9Ee.\-]? @exit ); -}%% - -static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result) -{ - int cs = EVIL; - - %% write init; - json->memo = p; - %% write exec; - - if (cs >= JSON_float_first_final) { - VALUE mod = Qnil; - ID method_id = 0; - if (rb_respond_to(json->decimal_class, i_try_convert)) { - mod = json->decimal_class; - method_id = i_try_convert; - } else if (rb_respond_to(json->decimal_class, i_new)) { - mod = json->decimal_class; - method_id = i_new; - } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) { - VALUE name = rb_class_name(json->decimal_class); - const char *name_cstr = RSTRING_PTR(name); - const char *last_colon = strrchr(name_cstr, ':'); - if (last_colon) { - const char *mod_path_end = last_colon - 1; - VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr); - mod = rb_path_to_class(mod_path); - - const char *method_name_beg = last_colon + 1; - long before_len = method_name_beg - name_cstr; - long len = RSTRING_LEN(name) - before_len; - VALUE method_name = rb_str_substr(name, before_len, len); - method_id = SYM2ID(rb_str_intern(method_name)); - } else { - mod = rb_mKernel; - method_id = SYM2ID(rb_str_intern(name)); - } - } - - long len = p - json->memo; - fbuffer_clear(json->fbuffer); - fbuffer_append(json->fbuffer, json->memo, len); - fbuffer_append_char(json->fbuffer, '\0'); - - if (method_id) { - VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer)); - *result = rb_funcallv(mod, method_id, 1, &text); - } else { - *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1)); - } - - return p + 1; - } else { - return NULL; - } -} - - -%%{ - machine JSON_array; - include JSON_common; - - write data; - - action parse_value { - VALUE v = Qnil; - char *np = JSON_parse_value(json, fpc, pe, &v, current_nesting); - if (np == NULL) { - fhold; fbreak; - } else { - if (NIL_P(json->array_class)) { - rb_ary_push(*result, v); - } else { - rb_funcall(*result, i_leftshift, 1, v); - } - fexec np; - } - } - - action exit { fhold; fbreak; } - - next_element = value_separator ignore* begin_value >parse_value; - - main := begin_array ignore* - ((begin_value >parse_value ignore*) - (ignore* next_element ignore*)*)? - end_array @exit; -}%% - -static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) -{ - int cs = EVIL; - VALUE array_class = json->array_class; - - if (json->max_nesting && current_nesting > json->max_nesting) { - rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); - } - *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class); - - %% write init; - %% write exec; - - if(cs >= JSON_array_first_final) { - return p + 1; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); - return NULL; - } -} - -static const size_t MAX_STACK_BUFFER_SIZE = 128; -static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int symbolize) -{ - VALUE result = Qnil; - size_t bufferSize = stringEnd - string; - char *p = string, *pe = string, *unescape, *bufferStart, *buffer; - int unescape_len; - char buf[4]; - - if (bufferSize > MAX_STACK_BUFFER_SIZE) { -# ifdef HAVE_RB_ENC_INTERNED_STR - bufferStart = buffer = ALLOC_N(char, bufferSize ? bufferSize : 1); -# else - bufferStart = buffer = ALLOC_N(char, bufferSize); -# endif - } else { -# ifdef HAVE_RB_ENC_INTERNED_STR - bufferStart = buffer = ALLOCA_N(char, bufferSize ? bufferSize : 1); -# else - bufferStart = buffer = ALLOCA_N(char, bufferSize); -# endif - } - - while (pe < stringEnd) { - if (*pe == '\\') { - unescape = (char *) "?"; - unescape_len = 1; - if (pe > p) { - MEMCPY(buffer, p, char, pe - p); - buffer += pe - p; - } - switch (*++pe) { - case 'n': - unescape = (char *) "\n"; - break; - case 'r': - unescape = (char *) "\r"; - break; - case 't': - unescape = (char *) "\t"; - break; - case '"': - unescape = (char *) "\""; - break; - case '\\': - unescape = (char *) "\\"; - break; - case 'b': - unescape = (char *) "\b"; - break; - case 'f': - unescape = (char *) "\f"; - break; - case 'u': - if (pe > stringEnd - 4) { - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - ruby_xfree(bufferStart); - } - rb_enc_raise( - EXC_ENCODING eParserError, - "incomplete unicode character escape sequence at '%s'", p - ); - } else { - UTF32 ch = unescape_unicode((unsigned char *) ++pe); - pe += 3; - if (UNI_SUR_HIGH_START == (ch & 0xFC00)) { - pe++; - if (pe > stringEnd - 6) { - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - ruby_xfree(bufferStart); - } - rb_enc_raise( - EXC_ENCODING eParserError, - "incomplete surrogate pair at '%s'", p - ); - } - if (pe[0] == '\\' && pe[1] == 'u') { - UTF32 sur = unescape_unicode((unsigned char *) pe + 2); - ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) - | (sur & 0x3FF)); - pe += 5; - } else { - unescape = (char *) "?"; - break; - } - } - unescape_len = convert_UTF32_to_UTF8(buf, ch); - unescape = buf; - } - break; - default: - p = pe; - continue; - } - MEMCPY(buffer, unescape, char, unescape_len); - buffer += unescape_len; - p = ++pe; - } else { - pe++; - } - } - - if (pe > p) { - MEMCPY(buffer, p, char, pe - p); - buffer += pe - p; - } - -# ifdef HAVE_RB_ENC_INTERNED_STR - if (intern) { - result = rb_enc_interned_str(bufferStart, (long)(buffer - bufferStart), rb_utf8_encoding()); - } else { - result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); - } - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - ruby_xfree(bufferStart); - } -# else - result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); - - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - ruby_xfree(bufferStart); - } - - if (intern) { - # if STR_UMINUS_DEDUPE_FROZEN - // Starting from MRI 2.8 it is preferable to freeze the string - // before deduplication so that it can be interned directly - // otherwise it would be duplicated first which is wasteful. - result = rb_funcall(rb_str_freeze(result), i_uminus, 0); - # elif STR_UMINUS_DEDUPE - // MRI 2.5 and older do not deduplicate strings that are already - // frozen. - result = rb_funcall(result, i_uminus, 0); - # else - result = rb_str_freeze(result); - # endif - } -# endif - - if (symbolize) { - result = rb_str_intern(result); - } - - return result; -} - -%%{ - machine JSON_string; - include JSON_common; - - write data; - - action parse_string { - *result = json_string_unescape(json->memo + 1, p, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names); - if (NIL_P(*result)) { - fhold; - fbreak; - } else { - fexec p + 1; - } - } - - action exit { fhold; fbreak; } - - main := '"' ((^([\"\\] | 0..0x1f) | '\\'[\"\\/bfnrt] | '\\u'[0-9a-fA-F]{4} | '\\'^([\"\\/bfnrtu]|0..0x1f))* %parse_string) '"' @exit; -}%% - -static int -match_i(VALUE regexp, VALUE klass, VALUE memo) -{ - if (regexp == Qundef) return ST_STOP; - if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) && - RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) { - rb_ary_push(memo, klass); - return ST_STOP; - } - return ST_CONTINUE; -} - -static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result) -{ - int cs = EVIL; - VALUE match_string; - - %% write init; - json->memo = p; - %% write exec; - - if (json->create_additions && RTEST(match_string = json->match_string)) { - VALUE klass; - VALUE memo = rb_ary_new2(2); - rb_ary_push(memo, *result); - rb_hash_foreach(match_string, match_i, memo); - klass = rb_ary_entry(memo, 1); - if (RTEST(klass)) { - *result = rb_funcall(klass, i_json_create, 1, *result); - } - } - - if (cs >= JSON_string_first_final) { - return p + 1; - } else { - return NULL; - } -} - -/* - * Document-class: JSON::Ext::Parser - * - * This is the JSON parser implemented as a C extension. It can be configured - * to be used by setting - * - * JSON.parser = JSON::Ext::Parser - * - * with the method parser= in JSON. - * - */ - -static VALUE convert_encoding(VALUE source) -{ -#ifdef HAVE_RUBY_ENCODING_H - rb_encoding *enc = rb_enc_get(source); - if (enc == rb_ascii8bit_encoding()) { - if (OBJ_FROZEN(source)) { - source = rb_str_dup(source); - } - FORCE_UTF8(source); - } else { - source = rb_str_conv_enc(source, rb_enc_get(source), rb_utf8_encoding()); - } -#endif - return source; -} - -/* - * call-seq: new(source, opts => {}) - * - * Creates a new JSON::Ext::Parser instance for the string _source_. - * - * It will be configured by the _opts_ hash. _opts_ can have the following - * keys: - * - * _opts_ can have the following keys: - * * *max_nesting*: The maximum depth of nesting allowed in the parsed data - * structures. Disable depth checking with :max_nesting => false|nil|0, it - * defaults to 100. - * * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in - * defiance of RFC 4627 to be parsed by the Parser. This option defaults to - * false. - * * *symbolize_names*: If set to true, returns symbols for the names - * (keys) in a JSON object. Otherwise strings are returned, which is - * also the default. It's not possible to use this option in - * conjunction with the *create_additions* option. - * * *create_additions*: If set to false, the Parser doesn't create - * additions even if a matching class and create_id was found. This option - * defaults to false. - * * *object_class*: Defaults to Hash - * * *array_class*: Defaults to Array - */ -static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) -{ - VALUE source, opts; - GET_PARSER_INIT; - - if (json->Vsource) { - rb_raise(rb_eTypeError, "already initialized instance"); - } - rb_scan_args(argc, argv, "1:", &source, &opts); - if (!NIL_P(opts)) { - VALUE tmp = ID2SYM(i_max_nesting); - if (option_given_p(opts, tmp)) { - VALUE max_nesting = rb_hash_aref(opts, tmp); - if (RTEST(max_nesting)) { - Check_Type(max_nesting, T_FIXNUM); - json->max_nesting = FIX2INT(max_nesting); - } else { - json->max_nesting = 0; - } - } else { - json->max_nesting = 100; - } - tmp = ID2SYM(i_allow_nan); - if (option_given_p(opts, tmp)) { - json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; - } else { - json->allow_nan = 0; - } - tmp = ID2SYM(i_symbolize_names); - if (option_given_p(opts, tmp)) { - json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; - } else { - json->symbolize_names = 0; - } - tmp = ID2SYM(i_freeze); - if (option_given_p(opts, tmp)) { - json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; - } else { - json->freeze = 0; - } - tmp = ID2SYM(i_create_additions); - if (option_given_p(opts, tmp)) { - json->create_additions = RTEST(rb_hash_aref(opts, tmp)); - } else { - json->create_additions = 0; - } - if (json->symbolize_names && json->create_additions) { - rb_raise(rb_eArgError, - "options :symbolize_names and :create_additions cannot be " - " used in conjunction"); - } - tmp = ID2SYM(i_create_id); - if (option_given_p(opts, tmp)) { - json->create_id = rb_hash_aref(opts, tmp); - } else { - json->create_id = rb_funcall(mJSON, i_create_id, 0); - } - tmp = ID2SYM(i_object_class); - if (option_given_p(opts, tmp)) { - json->object_class = rb_hash_aref(opts, tmp); - } else { - json->object_class = Qnil; - } - tmp = ID2SYM(i_array_class); - if (option_given_p(opts, tmp)) { - json->array_class = rb_hash_aref(opts, tmp); - } else { - json->array_class = Qnil; - } - tmp = ID2SYM(i_decimal_class); - if (option_given_p(opts, tmp)) { - json->decimal_class = rb_hash_aref(opts, tmp); - } else { - json->decimal_class = Qnil; - } - tmp = ID2SYM(i_match_string); - if (option_given_p(opts, tmp)) { - VALUE match_string = rb_hash_aref(opts, tmp); - json->match_string = RTEST(match_string) ? match_string : Qnil; - } else { - json->match_string = Qnil; - } - } else { - json->max_nesting = 100; - json->allow_nan = 0; - json->create_additions = 0; - json->create_id = Qnil; - json->object_class = Qnil; - json->array_class = Qnil; - json->decimal_class = Qnil; - } - source = convert_encoding(StringValue(source)); - StringValue(source); - json->len = RSTRING_LEN(source); - json->source = RSTRING_PTR(source);; - json->Vsource = source; - return self; -} - -%%{ - machine JSON; - - write data; - - include JSON_common; - - action parse_value { - char *np = JSON_parse_value(json, fpc, pe, &result, 0); - if (np == NULL) { fhold; fbreak; } else fexec np; - } - - main := ignore* ( - begin_value >parse_value - ) ignore*; -}%% - -/* - * call-seq: parse() - * - * Parses the current JSON text _source_ and returns the complete data - * structure as a result. - * It raises JSON::ParserError if fail to parse. - */ -static VALUE cParser_parse(VALUE self) -{ - char *p, *pe; - int cs = EVIL; - VALUE result = Qnil; - GET_PARSER; - - %% write init; - p = json->source; - pe = p + json->len; - %% write exec; - - if (cs >= JSON_first_final && p == pe) { - return result; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); - return Qnil; - } -} - -static void JSON_mark(void *ptr) -{ - JSON_Parser *json = ptr; - rb_gc_mark_maybe(json->Vsource); - rb_gc_mark_maybe(json->create_id); - rb_gc_mark_maybe(json->object_class); - rb_gc_mark_maybe(json->array_class); - rb_gc_mark_maybe(json->decimal_class); - rb_gc_mark_maybe(json->match_string); -} - -static void JSON_free(void *ptr) -{ - JSON_Parser *json = ptr; - fbuffer_free(json->fbuffer); - ruby_xfree(json); -} - -static size_t JSON_memsize(const void *ptr) -{ - const JSON_Parser *json = ptr; - return sizeof(*json) + FBUFFER_CAPA(json->fbuffer); -} - -#ifdef NEW_TYPEDDATA_WRAPPER -static const rb_data_type_t JSON_Parser_type = { - "JSON/Parser", - {JSON_mark, JSON_free, JSON_memsize,}, -#ifdef RUBY_TYPED_FREE_IMMEDIATELY - 0, 0, - RUBY_TYPED_FREE_IMMEDIATELY, -#endif -}; -#endif - -static VALUE cJSON_parser_s_allocate(VALUE klass) -{ - JSON_Parser *json; - VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json); - json->fbuffer = fbuffer_alloc(0); - return obj; -} - -/* - * call-seq: source() - * - * Returns a copy of the current _source_ string, that was used to construct - * this Parser. - */ -static VALUE cParser_source(VALUE self) -{ - GET_PARSER; - return rb_str_dup(json->Vsource); -} - -void Init_parser(void) -{ -#ifdef HAVE_RB_EXT_RACTOR_SAFE - rb_ext_ractor_safe(true); -#endif - -#undef rb_intern - rb_require("json/common"); - mJSON = rb_define_module("JSON"); - mExt = rb_define_module_under(mJSON, "Ext"); - cParser = rb_define_class_under(mExt, "Parser", rb_cObject); - eParserError = rb_path2class("JSON::ParserError"); - eNestingError = rb_path2class("JSON::NestingError"); - rb_gc_register_mark_object(eParserError); - rb_gc_register_mark_object(eNestingError); - rb_define_alloc_func(cParser, cJSON_parser_s_allocate); - rb_define_method(cParser, "initialize", cParser_initialize, -1); - rb_define_method(cParser, "parse", cParser_parse, 0); - rb_define_method(cParser, "source", cParser_source, 0); - - CNaN = rb_const_get(mJSON, rb_intern("NaN")); - rb_gc_register_mark_object(CNaN); - - CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); - rb_gc_register_mark_object(CInfinity); - - CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity")); - rb_gc_register_mark_object(CMinusInfinity); - - i_json_creatable_p = rb_intern("json_creatable?"); - i_json_create = rb_intern("json_create"); - i_create_id = rb_intern("create_id"); - i_create_additions = rb_intern("create_additions"); - i_chr = rb_intern("chr"); - i_max_nesting = rb_intern("max_nesting"); - i_allow_nan = rb_intern("allow_nan"); - i_symbolize_names = rb_intern("symbolize_names"); - i_object_class = rb_intern("object_class"); - i_array_class = rb_intern("array_class"); - i_decimal_class = rb_intern("decimal_class"); - i_match = rb_intern("match"); - i_match_string = rb_intern("match_string"); - i_key_p = rb_intern("key?"); - i_deep_const_get = rb_intern("deep_const_get"); - i_aset = rb_intern("[]="); - i_aref = rb_intern("[]"); - i_leftshift = rb_intern("<<"); - i_new = rb_intern("new"); - i_try_convert = rb_intern("try_convert"); - i_freeze = rb_intern("freeze"); - i_uminus = rb_intern("-@"); -} - -/* - * Local variables: - * mode: c - * c-file-style: ruby - * indent-tabs-mode: nil - * End: - */ diff --git a/ext/json/parser/prereq.mk b/ext/json/parser/prereq.mk deleted file mode 100644 index fc59169056..0000000000 --- a/ext/json/parser/prereq.mk +++ /dev/null @@ -1,13 +0,0 @@ -RAGEL = ragel - -.SUFFIXES: .rl - -.rl.c: - $(RAGEL) -G2 $< - $(BASERUBY) -pli -e '$$_.sub!(/[ \t]+$$/, "")' \ - -e '$$_.sub!(/^static const int (JSON_.*=.*);$$/, "enum {\\1};")' \ - -e '$$_.sub!(/^(static const char) (_JSON(?:_\w+)?_nfa_\w+)(?=\[\] =)/, "\\1 MAYBE_UNUSED(\\2)")' \ - -e '$$_.sub!(/0 <= ([\( ]+\*[\( ]*p\)+) && \1 <= 31/, "0 <= (signed char)(*(p)) && (*(p)) <= 31")' \ - -e '$$_ = "/* This file is automatically generated from parser.rl by using ragel */\n" + $$_ if $$. == 1' $@ - -parser.c: diff --git a/ext/json/simd/conf.rb b/ext/json/simd/conf.rb new file mode 100644 index 0000000000..76f774bc97 --- /dev/null +++ b/ext/json/simd/conf.rb @@ -0,0 +1,24 @@ +case RbConfig::CONFIG['host_cpu'] +when /^(arm|aarch64)/ + # Try to compile a small program using NEON instructions + header, type, init, extra = 'arm_neon.h', 'uint8x16_t', 'vdupq_n_u8(32)', nil +when /^(x86_64|x64)/ + header, type, init, extra = 'x86intrin.h', '__m128i', '_mm_set1_epi8(32)', 'if (__builtin_cpu_supports("sse2")) { printf("OK"); }' +end +if header + if have_header(header) && try_compile(<<~SRC, '-Werror=implicit-function-declaration') + #{cpp_include(header)} + int main(int argc, char **argv) { + #{type} test = #{init}; + #{extra} + if (argc > 100000) printf("%p", &test); + return 0; + } + SRC + $defs.push("-DJSON_ENABLE_SIMD") + else + puts "Disable SIMD" + end +end + +have_header('cpuid.h') diff --git a/ext/json/simd/simd.h b/ext/json/simd/simd.h new file mode 100644 index 0000000000..3bb86acdec --- /dev/null +++ b/ext/json/simd/simd.h @@ -0,0 +1,218 @@ +#include "../json.h" + +typedef enum { + SIMD_NONE, + SIMD_NEON, + SIMD_SSE2 +} SIMD_Implementation; + +#ifndef __has_builtin // Optional of course. + #define __has_builtin(x) 0 // Compatibility with non-clang compilers. +#endif + +#ifdef __clang__ +# if __has_builtin(__builtin_ctzll) +# define HAVE_BUILTIN_CTZLL 1 +# else +# define HAVE_BUILTIN_CTZLL 0 +# endif +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) +# define HAVE_BUILTIN_CTZLL 1 +#else +# define HAVE_BUILTIN_CTZLL 0 +#endif + +static inline uint32_t trailing_zeros64(uint64_t input) +{ + JSON_ASSERT(input > 0); // __builtin_ctz(0) is undefined behavior + +#if HAVE_BUILTIN_CTZLL + return __builtin_ctzll(input); +#else + uint32_t trailing_zeros = 0; + uint64_t temp = input; + while ((temp & 1) == 0 && temp > 0) { + trailing_zeros++; + temp >>= 1; + } + return trailing_zeros; +#endif +} + +static inline int trailing_zeros(int input) +{ + JSON_ASSERT(input > 0); // __builtin_ctz(0) is undefined behavior + +#if HAVE_BUILTIN_CTZLL + return __builtin_ctz(input); +#else + int trailing_zeros = 0; + int temp = input; + while ((temp & 1) == 0 && temp > 0) { + trailing_zeros++; + temp >>= 1; + } + return trailing_zeros; +#endif +} + +#ifdef JSON_ENABLE_SIMD + +#define SIMD_MINIMUM_THRESHOLD 4 + +ALWAYS_INLINE(static) void json_fast_memcpy16(char *dst, const char *src, size_t len) +{ + RBIMPL_ASSERT_OR_ASSUME(len < 16); + RBIMPL_ASSERT_OR_ASSUME(len >= SIMD_MINIMUM_THRESHOLD); // 4 +#if defined(__has_builtin) && __has_builtin(__builtin_memcpy) + // If __builtin_memcpy is available, use it to copy between SIMD_MINIMUM_THRESHOLD (4) and vec_len-1 (15) bytes. + // These copies overlap. The first copy will copy the first 8 (or 4) bytes. The second copy will copy + // the last 8 (or 4) bytes but overlap with the first copy. The overlapping bytes will be in the correct + // position in both copies. + + // Please do not attempt to replace __builtin_memcpy with memcpy without profiling and/or looking at the + // generated assembly. On clang-specifically (tested on Apple clang version 17.0.0 (clang-1700.0.13.3)), + // when using memcpy, the compiler will notice the only difference is a 4 or 8 and generate a conditional + // select instruction instead of direct loads and stores with a branch. This ends up slower than the branch + // plus two loads and stores generated when using __builtin_memcpy. + if (len >= 8) { + __builtin_memcpy(dst, src, 8); + __builtin_memcpy(dst + len - 8, src + len - 8, 8); + } else { + __builtin_memcpy(dst, src, 4); + __builtin_memcpy(dst + len - 4, src + len - 4, 4); + } +#else + MEMCPY(dst, src, char, len); +#endif +} + +#if defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(__aarch64__) || defined(_M_ARM64) +#include <arm_neon.h> + +#define FIND_SIMD_IMPLEMENTATION_DEFINED 1 +static inline SIMD_Implementation find_simd_implementation(void) +{ + return SIMD_NEON; +} + +#define HAVE_SIMD 1 +#define HAVE_SIMD_NEON 1 + +// See: https://community.arm.com/arm-community-blogs/b/servers-and-cloud-computing-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon +ALWAYS_INLINE(static) uint64_t neon_match_mask(uint8x16_t matches) +{ + const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(matches), 4); + const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0); + return mask & 0x8888888888888888ull; +} + +ALWAYS_INLINE(static) uint64_t compute_chunk_mask_neon(const char *ptr) +{ + uint8x16_t chunk = vld1q_u8((const unsigned char *)ptr); + + // Trick: c < 32 || c == 34 can be factored as c ^ 2 < 33 + // https://lemire.me/blog/2025/04/13/detect-control-characters-quotes-and-backslashes-efficiently-using-swar/ + const uint8x16_t too_low_or_dbl_quote = vcltq_u8(veorq_u8(chunk, vdupq_n_u8(2)), vdupq_n_u8(33)); + + uint8x16_t has_backslash = vceqq_u8(chunk, vdupq_n_u8('\\')); + uint8x16_t needs_escape = vorrq_u8(too_low_or_dbl_quote, has_backslash); + return neon_match_mask(needs_escape); +} + +ALWAYS_INLINE(static) int string_scan_simd_neon(const char **ptr, const char *end, uint64_t *mask) +{ + while (*ptr + sizeof(uint8x16_t) <= end) { + uint64_t chunk_mask = compute_chunk_mask_neon(*ptr); + if (chunk_mask) { + *mask = chunk_mask; + return 1; + } + *ptr += sizeof(uint8x16_t); + } + return 0; +} + +static inline uint8x16x4_t load_uint8x16_4(const unsigned char *table) +{ + uint8x16x4_t tab; + tab.val[0] = vld1q_u8(table); + tab.val[1] = vld1q_u8(table+16); + tab.val[2] = vld1q_u8(table+32); + tab.val[3] = vld1q_u8(table+48); + return tab; +} + +#endif /* ARM Neon Support.*/ + +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) + +#ifdef HAVE_X86INTRIN_H +#include <x86intrin.h> + +#define HAVE_SIMD 1 +#define HAVE_SIMD_SSE2 1 + +#ifdef HAVE_CPUID_H +#define FIND_SIMD_IMPLEMENTATION_DEFINED 1 + +#if defined(__clang__) || defined(__GNUC__) +#define TARGET_SSE2 __attribute__((target("sse2"))) +#else +#define TARGET_SSE2 +#endif + +#define _mm_cmpge_epu8(a, b) _mm_cmpeq_epi8(_mm_max_epu8(a, b), a) +#define _mm_cmple_epu8(a, b) _mm_cmpge_epu8(b, a) +#define _mm_cmpgt_epu8(a, b) _mm_xor_si128(_mm_cmple_epu8(a, b), _mm_set1_epi8(-1)) +#define _mm_cmplt_epu8(a, b) _mm_cmpgt_epu8(b, a) + +ALWAYS_INLINE(static) TARGET_SSE2 int compute_chunk_mask_sse2(const char *ptr) +{ + __m128i chunk = _mm_loadu_si128((__m128i const*)ptr); + // Trick: c < 32 || c == 34 can be factored as c ^ 2 < 33 + // https://lemire.me/blog/2025/04/13/detect-control-characters-quotes-and-backslashes-efficiently-using-swar/ + __m128i too_low_or_dbl_quote = _mm_cmplt_epu8(_mm_xor_si128(chunk, _mm_set1_epi8(2)), _mm_set1_epi8(33)); + __m128i has_backslash = _mm_cmpeq_epi8(chunk, _mm_set1_epi8('\\')); + __m128i needs_escape = _mm_or_si128(too_low_or_dbl_quote, has_backslash); + return _mm_movemask_epi8(needs_escape); +} + +ALWAYS_INLINE(static) TARGET_SSE2 int string_scan_simd_sse2(const char **ptr, const char *end, int *mask) +{ + while (*ptr + sizeof(__m128i) <= end) { + int chunk_mask = compute_chunk_mask_sse2(*ptr); + if (chunk_mask) { + *mask = chunk_mask; + return 1; + } + *ptr += sizeof(__m128i); + } + + return 0; +} + +#include <cpuid.h> +#endif /* HAVE_CPUID_H */ + +static inline SIMD_Implementation find_simd_implementation(void) +{ + // TODO Revisit. I think the SSE version now only uses SSE2 instructions. + if (__builtin_cpu_supports("sse2")) { + return SIMD_SSE2; + } + + return SIMD_NONE; +} + +#endif /* HAVE_X86INTRIN_H */ +#endif /* X86_64 Support */ + +#endif /* JSON_ENABLE_SIMD */ + +#ifndef FIND_SIMD_IMPLEMENTATION_DEFINED +static inline SIMD_Implementation find_simd_implementation(void) +{ + return SIMD_NONE; +} +#endif diff --git a/ext/json/vendor/fpconv.c b/ext/json/vendor/fpconv.c new file mode 100644 index 0000000000..6c9bc2c103 --- /dev/null +++ b/ext/json/vendor/fpconv.c @@ -0,0 +1,480 @@ +// Boost Software License - Version 1.0 - August 17th, 2003 +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +// The contents of this file is extracted from https://github.com/night-shift/fpconv +// It was slightly modified to append ".0" to plain floats, for use with the https://github.com/ruby/json package. + +#include <stdbool.h> +#include <string.h> +#include <stdint.h> + +#if JSON_DEBUG +#include <assert.h> +#endif + +#define npowers 87 +#define steppowers 8 +#define firstpower -348 /* 10 ^ -348 */ + +#define expmax -32 +#define expmin -60 + +typedef struct Fp { + uint64_t frac; + int exp; +} Fp; + +static const Fp powers_ten[] = { + { 18054884314459144840U, -1220 }, { 13451937075301367670U, -1193 }, + { 10022474136428063862U, -1166 }, { 14934650266808366570U, -1140 }, + { 11127181549972568877U, -1113 }, { 16580792590934885855U, -1087 }, + { 12353653155963782858U, -1060 }, { 18408377700990114895U, -1034 }, + { 13715310171984221708U, -1007 }, { 10218702384817765436U, -980 }, + { 15227053142812498563U, -954 }, { 11345038669416679861U, -927 }, + { 16905424996341287883U, -901 }, { 12595523146049147757U, -874 }, + { 9384396036005875287U, -847 }, { 13983839803942852151U, -821 }, + { 10418772551374772303U, -794 }, { 15525180923007089351U, -768 }, + { 11567161174868858868U, -741 }, { 17236413322193710309U, -715 }, + { 12842128665889583758U, -688 }, { 9568131466127621947U, -661 }, + { 14257626930069360058U, -635 }, { 10622759856335341974U, -608 }, + { 15829145694278690180U, -582 }, { 11793632577567316726U, -555 }, + { 17573882009934360870U, -529 }, { 13093562431584567480U, -502 }, + { 9755464219737475723U, -475 }, { 14536774485912137811U, -449 }, + { 10830740992659433045U, -422 }, { 16139061738043178685U, -396 }, + { 12024538023802026127U, -369 }, { 17917957937422433684U, -343 }, + { 13349918974505688015U, -316 }, { 9946464728195732843U, -289 }, + { 14821387422376473014U, -263 }, { 11042794154864902060U, -236 }, + { 16455045573212060422U, -210 }, { 12259964326927110867U, -183 }, + { 18268770466636286478U, -157 }, { 13611294676837538539U, -130 }, + { 10141204801825835212U, -103 }, { 15111572745182864684U, -77 }, + { 11258999068426240000U, -50 }, { 16777216000000000000U, -24 }, + { 12500000000000000000U, 3 }, { 9313225746154785156U, 30 }, + { 13877787807814456755U, 56 }, { 10339757656912845936U, 83 }, + { 15407439555097886824U, 109 }, { 11479437019748901445U, 136 }, + { 17105694144590052135U, 162 }, { 12744735289059618216U, 189 }, + { 9495567745759798747U, 216 }, { 14149498560666738074U, 242 }, + { 10542197943230523224U, 269 }, { 15709099088952724970U, 295 }, + { 11704190886730495818U, 322 }, { 17440603504673385349U, 348 }, + { 12994262207056124023U, 375 }, { 9681479787123295682U, 402 }, + { 14426529090290212157U, 428 }, { 10748601772107342003U, 455 }, + { 16016664761464807395U, 481 }, { 11933345169920330789U, 508 }, + { 17782069995880619868U, 534 }, { 13248674568444952270U, 561 }, + { 9871031767461413346U, 588 }, { 14708983551653345445U, 614 }, + { 10959046745042015199U, 641 }, { 16330252207878254650U, 667 }, + { 12166986024289022870U, 694 }, { 18130221999122236476U, 720 }, + { 13508068024458167312U, 747 }, { 10064294952495520794U, 774 }, + { 14996968138956309548U, 800 }, { 11173611982879273257U, 827 }, + { 16649979327439178909U, 853 }, { 12405201291620119593U, 880 }, + { 9242595204427927429U, 907 }, { 13772540099066387757U, 933 }, + { 10261342003245940623U, 960 }, { 15290591125556738113U, 986 }, + { 11392378155556871081U, 1013 }, { 16975966327722178521U, 1039 }, + { 12648080533535911531U, 1066 } +}; + +static Fp find_cachedpow10(int exp, int* k) +{ + const double one_log_ten = 0.30102999566398114; + + int approx = (int)(-(exp + npowers) * one_log_ten); + int idx = (approx - firstpower) / steppowers; + + while(1) { + int current = exp + powers_ten[idx].exp + 64; + + if(current < expmin) { + idx++; + continue; + } + + if(current > expmax) { + idx--; + continue; + } + + *k = (firstpower + idx * steppowers); + + return powers_ten[idx]; + } +} + +#define fracmask 0x000FFFFFFFFFFFFFU +#define expmask 0x7FF0000000000000U +#define hiddenbit 0x0010000000000000U +#define signmask 0x8000000000000000U +#define expbias (1023 + 52) + +#define absv(n) ((n) < 0 ? -(n) : (n)) +#define minv(a, b) ((a) < (b) ? (a) : (b)) + +static const uint64_t tens[] = { + 10000000000000000000U, 1000000000000000000U, 100000000000000000U, + 10000000000000000U, 1000000000000000U, 100000000000000U, + 10000000000000U, 1000000000000U, 100000000000U, + 10000000000U, 1000000000U, 100000000U, + 10000000U, 1000000U, 100000U, + 10000U, 1000U, 100U, + 10U, 1U +}; + +static inline uint64_t get_dbits(double d) +{ + union { + double dbl; + uint64_t i; + } dbl_bits = { d }; + + return dbl_bits.i; +} + +static Fp build_fp(double d) +{ + uint64_t bits = get_dbits(d); + + Fp fp; + fp.frac = bits & fracmask; + fp.exp = (bits & expmask) >> 52; + + if(fp.exp) { + fp.frac += hiddenbit; + fp.exp -= expbias; + + } else { + fp.exp = -expbias + 1; + } + + return fp; +} + +static void normalize(Fp* fp) +{ + while ((fp->frac & hiddenbit) == 0) { + fp->frac <<= 1; + fp->exp--; + } + + int shift = 64 - 52 - 1; + fp->frac <<= shift; + fp->exp -= shift; +} + +static void get_normalized_boundaries(Fp* fp, Fp* lower, Fp* upper) +{ + upper->frac = (fp->frac << 1) + 1; + upper->exp = fp->exp - 1; + + while ((upper->frac & (hiddenbit << 1)) == 0) { + upper->frac <<= 1; + upper->exp--; + } + + int u_shift = 64 - 52 - 2; + + upper->frac <<= u_shift; + upper->exp = upper->exp - u_shift; + + + int l_shift = fp->frac == hiddenbit ? 2 : 1; + + lower->frac = (fp->frac << l_shift) - 1; + lower->exp = fp->exp - l_shift; + + + lower->frac <<= lower->exp - upper->exp; + lower->exp = upper->exp; +} + +static Fp multiply(Fp* a, Fp* b) +{ + const uint64_t lomask = 0x00000000FFFFFFFF; + + uint64_t ah_bl = (a->frac >> 32) * (b->frac & lomask); + uint64_t al_bh = (a->frac & lomask) * (b->frac >> 32); + uint64_t al_bl = (a->frac & lomask) * (b->frac & lomask); + uint64_t ah_bh = (a->frac >> 32) * (b->frac >> 32); + + uint64_t tmp = (ah_bl & lomask) + (al_bh & lomask) + (al_bl >> 32); + /* round up */ + tmp += 1U << 31; + + Fp fp = { + ah_bh + (ah_bl >> 32) + (al_bh >> 32) + (tmp >> 32), + a->exp + b->exp + 64 + }; + + return fp; +} + +static void round_digit(char* digits, int ndigits, uint64_t delta, uint64_t rem, uint64_t kappa, uint64_t frac) +{ + while (rem < frac && delta - rem >= kappa && + (rem + kappa < frac || frac - rem > rem + kappa - frac)) { + + digits[ndigits - 1]--; + rem += kappa; + } +} + +static int generate_digits(Fp* fp, Fp* upper, Fp* lower, char* digits, int* K) +{ + uint64_t wfrac = upper->frac - fp->frac; + uint64_t delta = upper->frac - lower->frac; + + Fp one; + one.frac = 1ULL << -upper->exp; + one.exp = upper->exp; + + uint64_t part1 = upper->frac >> -one.exp; + uint64_t part2 = upper->frac & (one.frac - 1); + + int idx = 0, kappa = 10; + const uint64_t* divp; + /* 1000000000 */ + for(divp = tens + 10; kappa > 0; divp++) { + + uint64_t div = *divp; + unsigned digit = (unsigned) (part1 / div); + + if (digit || idx) { + digits[idx++] = digit + '0'; + } + + part1 -= digit * div; + kappa--; + + uint64_t tmp = (part1 <<-one.exp) + part2; + if (tmp <= delta) { + *K += kappa; + round_digit(digits, idx, delta, tmp, div << -one.exp, wfrac); + + return idx; + } + } + + /* 10 */ + const uint64_t* unit = tens + 18; + + while(true) { + part2 *= 10; + delta *= 10; + kappa--; + + unsigned digit = (unsigned) (part2 >> -one.exp); + if (digit || idx) { + digits[idx++] = digit + '0'; + } + + part2 &= one.frac - 1; + if (part2 < delta) { + *K += kappa; + round_digit(digits, idx, delta, part2, one.frac, wfrac * *unit); + + return idx; + } + + unit--; + } +} + +static int grisu2(double d, char* digits, int* K) +{ + Fp w = build_fp(d); + + Fp lower, upper; + get_normalized_boundaries(&w, &lower, &upper); + + normalize(&w); + + int k; + Fp cp = find_cachedpow10(upper.exp, &k); + + w = multiply(&w, &cp); + upper = multiply(&upper, &cp); + lower = multiply(&lower, &cp); + + lower.frac++; + upper.frac--; + + *K = -k; + + return generate_digits(&w, &upper, &lower, digits, K); +} + +static int emit_digits(char* digits, int ndigits, char* dest, int K, bool neg) +{ + int exp = absv(K + ndigits - 1); + + if(K >= 0 && exp < 15) { + memcpy(dest, digits, ndigits); + memset(dest + ndigits, '0', K); + + /* add a .0 to mark this as a float. */ + dest[ndigits + K] = '.'; + dest[ndigits + K + 1] = '0'; + + return ndigits + K + 2; + } + + /* write decimal w/o scientific notation */ + if(K < 0 && (K > -7 || exp < 10)) { + int offset = ndigits - absv(K); + /* fp < 1.0 -> write leading zero */ + if(offset <= 0) { + offset = -offset; + dest[0] = '0'; + dest[1] = '.'; + memset(dest + 2, '0', offset); + memcpy(dest + offset + 2, digits, ndigits); + + return ndigits + 2 + offset; + + /* fp > 1.0 */ + } else { + memcpy(dest, digits, offset); + dest[offset] = '.'; + memcpy(dest + offset + 1, digits + offset, ndigits - offset); + + return ndigits + 1; + } + } + + /* write decimal w/ scientific notation */ + ndigits = minv(ndigits, 18 - neg); + + int idx = 0; + dest[idx++] = digits[0]; + + if(ndigits > 1) { + dest[idx++] = '.'; + memcpy(dest + idx, digits + 1, ndigits - 1); + idx += ndigits - 1; + } + + dest[idx++] = 'e'; + + char sign = K + ndigits - 1 < 0 ? '-' : '+'; + dest[idx++] = sign; + + int cent = 0; + + if(exp > 99) { + cent = exp / 100; + dest[idx++] = cent + '0'; + exp -= cent * 100; + } + if(exp > 9) { + int dec = exp / 10; + dest[idx++] = dec + '0'; + exp -= dec * 10; + + } else if(cent) { + dest[idx++] = '0'; + } + + dest[idx++] = exp % 10 + '0'; + + return idx; +} + +static int filter_special(double fp, char* dest) +{ + if(fp == 0.0) { + dest[0] = '0'; + dest[1] = '.'; + dest[2] = '0'; + return 3; + } + + uint64_t bits = get_dbits(fp); + + bool nan = (bits & expmask) == expmask; + + if(!nan) { + return 0; + } + + if(bits & fracmask) { + dest[0] = 'n'; dest[1] = 'a'; dest[2] = 'n'; + + } else { + dest[0] = 'i'; dest[1] = 'n'; dest[2] = 'f'; + } + + return 3; +} + +/* Fast and accurate double to string conversion based on Florian Loitsch's + * Grisu-algorithm[1]. + * + * Input: + * fp -> the double to convert, dest -> destination buffer. + * The generated string will never be longer than 32 characters. + * Make sure to pass a pointer to at least 32 bytes of memory. + * The emitted string will not be null terminated. + * + * + * + * Output: + * The number of written characters. + * + * Exemplary usage: + * + * void print(double d) + * { + * char buf[28 + 1] // plus null terminator + * int str_len = fpconv_dtoa(d, buf); + * + * buf[str_len] = '\0'; + * printf("%s", buf); + * } + * + */ +static int fpconv_dtoa(double d, char dest[32]) +{ + char digits[18]; + + int str_len = 0; + bool neg = false; + + if(get_dbits(d) & signmask) { + dest[0] = '-'; + str_len++; + neg = true; + } + + int spec = filter_special(d, dest + str_len); + + if(spec) { + return str_len + spec; + } + + int K = 0; + int ndigits = grisu2(d, digits, &K); + + str_len += emit_digits(digits, ndigits, dest + str_len, K, neg); +#if JSON_DEBUG + assert(str_len <= 32); +#endif + + return str_len; +} diff --git a/ext/json/vendor/jeaiii-ltoa.h b/ext/json/vendor/jeaiii-ltoa.h new file mode 100644 index 0000000000..ba4f497fc8 --- /dev/null +++ b/ext/json/vendor/jeaiii-ltoa.h @@ -0,0 +1,267 @@ +/* + +This file is released under the terms of the MIT License. It is based on the +work of James Edward Anhalt III, with the original license listed below. + +MIT License + +Copyright (c) 2024,2025 Enrico Thierbach - https://github.com/radiospiel +Copyright (c) 2022 James Edward Anhalt III - https://github.com/jeaiii/itoa + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef JEAIII_TO_TEXT_H_ +#define JEAIII_TO_TEXT_H_ + +#include <stdint.h> + +typedef uint_fast32_t u32_t; +typedef uint_fast64_t u64_t; + +#define u32(x) ((u32_t)(x)) +#define u64(x) ((u64_t)(x)) + +struct digit_pair +{ + char dd[2]; +}; + +static const struct digit_pair *digits_dd = (struct digit_pair *)( + "00" "01" "02" "03" "04" "05" "06" "07" "08" "09" + "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" + "20" "21" "22" "23" "24" "25" "26" "27" "28" "29" + "30" "31" "32" "33" "34" "35" "36" "37" "38" "39" + "40" "41" "42" "43" "44" "45" "46" "47" "48" "49" + "50" "51" "52" "53" "54" "55" "56" "57" "58" "59" + "60" "61" "62" "63" "64" "65" "66" "67" "68" "69" + "70" "71" "72" "73" "74" "75" "76" "77" "78" "79" + "80" "81" "82" "83" "84" "85" "86" "87" "88" "89" + "90" "91" "92" "93" "94" "95" "96" "97" "98" "99" +); + +static const struct digit_pair *digits_fd = (struct digit_pair *)( + "0_" "1_" "2_" "3_" "4_" "5_" "6_" "7_" "8_" "9_" + "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" + "20" "21" "22" "23" "24" "25" "26" "27" "28" "29" + "30" "31" "32" "33" "34" "35" "36" "37" "38" "39" + "40" "41" "42" "43" "44" "45" "46" "47" "48" "49" + "50" "51" "52" "53" "54" "55" "56" "57" "58" "59" + "60" "61" "62" "63" "64" "65" "66" "67" "68" "69" + "70" "71" "72" "73" "74" "75" "76" "77" "78" "79" + "80" "81" "82" "83" "84" "85" "86" "87" "88" "89" + "90" "91" "92" "93" "94" "95" "96" "97" "98" "99" +); + +static const u64_t mask24 = (u64(1) << 24) - 1; +static const u64_t mask32 = (u64(1) << 32) - 1; +static const u64_t mask57 = (u64(1) << 57) - 1; + +#define COPY(buffer, digits) memcpy(buffer, &(digits), sizeof(struct digit_pair)) + +static char * +jeaiii_ultoa(char *b, u64_t n) +{ + if (n < u32(1e2)) { + COPY(b, digits_fd[n]); + return n < 10 ? b + 1 : b + 2; + } + + if (n < u32(1e6)) { + if (n < u32(1e4)) { + u32_t f0 = u32((10 * (1 << 24) / 1e3 + 1) * n); + COPY(b, digits_fd[f0 >> 24]); + + b -= n < u32(1e3); + u32_t f2 = (f0 & mask24) * 100; + COPY(b + 2, digits_dd[f2 >> 24]); + + return b + 4; + } + + u64_t f0 = u64(10 * (1ull << 32ull)/ 1e5 + 1) * n; + COPY(b, digits_fd[f0 >> 32]); + + b -= n < u32(1e5); + u64_t f2 = (f0 & mask32) * 100; + COPY(b + 2, digits_dd[f2 >> 32]); + + u64_t f4 = (f2 & mask32) * 100; + COPY(b + 4, digits_dd[f4 >> 32]); + return b + 6; + } + + if (n < u64(1ull << 32ull)) { + if (n < u32(1e8)) { + u64_t f0 = u64(10 * (1ull << 48ull) / 1e7 + 1) * n >> 16; + COPY(b, digits_fd[f0 >> 32]); + + b -= n < u32(1e7); + u64_t f2 = (f0 & mask32) * 100; + COPY(b + 2, digits_dd[f2 >> 32]); + + u64_t f4 = (f2 & mask32) * 100; + COPY(b + 4, digits_dd[f4 >> 32]); + + u64_t f6 = (f4 & mask32) * 100; + COPY(b + 6, digits_dd[f6 >> 32]); + + return b + 8; + } + + u64_t f0 = u64(10 * (1ull << 57ull) / 1e9 + 1) * n; + COPY(b, digits_fd[f0 >> 57]); + + b -= n < u32(1e9); + u64_t f2 = (f0 & mask57) * 100; + COPY(b + 2, digits_dd[f2 >> 57]); + + u64_t f4 = (f2 & mask57) * 100; + COPY(b + 4, digits_dd[f4 >> 57]); + + u64_t f6 = (f4 & mask57) * 100; + COPY(b + 6, digits_dd[f6 >> 57]); + + u64_t f8 = (f6 & mask57) * 100; + COPY(b + 8, digits_dd[f8 >> 57]); + + return b + 10; + } + + // if we get here U must be u64 but some compilers don't know that, so reassign n to a u64 to avoid warnings + u32_t z = n % u32(1e8); + u64_t u = n / u32(1e8); + + if (u < u32(1e2)) { + // u can't be 1 digit (if u < 10 it would have been handled above as a 9 digit 32bit number) + COPY(b, digits_dd[u]); + b += 2; + } + else if (u < u32(1e6)) { + if (u < u32(1e4)) { + u32_t f0 = u32((10 * (1 << 24) / 1e3 + 1) * u); + COPY(b, digits_fd[f0 >> 24]); + + b -= u < u32(1e3); + u32_t f2 = (f0 & mask24) * 100; + COPY(b + 2, digits_dd[f2 >> 24]); + b += 4; + } + else { + u64_t f0 = u64(10 * (1ull << 32ull) / 1e5 + 1) * u; + COPY(b, digits_fd[f0 >> 32]); + + b -= u < u32(1e5); + u64_t f2 = (f0 & mask32) * 100; + COPY(b + 2, digits_dd[f2 >> 32]); + + u64_t f4 = (f2 & mask32) * 100; + COPY(b + 4, digits_dd[f4 >> 32]); + b += 6; + } + } + else if (u < u32(1e8)) { + u64_t f0 = u64(10 * (1ull << 48ull) / 1e7 + 1) * u >> 16; + COPY(b, digits_fd[f0 >> 32]); + + b -= u < u32(1e7); + u64_t f2 = (f0 & mask32) * 100; + COPY(b + 2, digits_dd[f2 >> 32]); + + u64_t f4 = (f2 & mask32) * 100; + COPY(b + 4, digits_dd[f4 >> 32]); + + u64_t f6 = (f4 & mask32) * 100; + COPY(b + 6, digits_dd[f6 >> 32]); + + b += 8; + } + else if (u < u64(1ull << 32ull)) { + u64_t f0 = u64(10 * (1ull << 57ull) / 1e9 + 1) * u; + COPY(b, digits_fd[f0 >> 57]); + + b -= u < u32(1e9); + u64_t f2 = (f0 & mask57) * 100; + COPY(b + 2, digits_dd[f2 >> 57]); + + u64_t f4 = (f2 & mask57) * 100; + COPY(b + 4, digits_dd[f4 >> 57]); + + u64_t f6 = (f4 & mask57) * 100; + COPY(b + 6, digits_dd[f6 >> 57]); + + u64_t f8 = (f6 & mask57) * 100; + COPY(b + 8, digits_dd[f8 >> 57]); + b += 10; + } + else { + u32_t y = u % u32(1e8); + u /= u32(1e8); + + // u is 2, 3, or 4 digits (if u < 10 it would have been handled above) + if (u < u32(1e2)) { + COPY(b, digits_dd[u]); + b += 2; + } + else { + u32_t f0 = u32((10 * (1 << 24) / 1e3 + 1) * u); + COPY(b, digits_fd[f0 >> 24]); + + b -= u < u32(1e3); + u32_t f2 = (f0 & mask24) * 100; + COPY(b + 2, digits_dd[f2 >> 24]); + + b += 4; + } + // do 8 digits + u64_t f0 = (u64((1ull << 48ull) / 1e6 + 1) * y >> 16) + 1; + COPY(b, digits_dd[f0 >> 32]); + + u64_t f2 = (f0 & mask32) * 100; + COPY(b + 2, digits_dd[f2 >> 32]); + + u64_t f4 = (f2 & mask32) * 100; + COPY(b + 4, digits_dd[f4 >> 32]); + + u64_t f6 = (f4 & mask32) * 100; + COPY(b + 6, digits_dd[f6 >> 32]); + b += 8; + } + + // do 8 digits + u64_t f0 = (u64((1ull << 48ull) / 1e6 + 1) * z >> 16) + 1; + COPY(b, digits_dd[f0 >> 32]); + + u64_t f2 = (f0 & mask32) * 100; + COPY(b + 2, digits_dd[f2 >> 32]); + + u64_t f4 = (f2 & mask32) * 100; + COPY(b + 4, digits_dd[f4 >> 32]); + + u64_t f6 = (f4 & mask32) * 100; + COPY(b + 6, digits_dd[f6 >> 32]); + + return b + 8; +} + +#undef u32 +#undef u64 +#undef COPY + +#endif // JEAIII_TO_TEXT_H_ diff --git a/ext/json/vendor/ryu.h b/ext/json/vendor/ryu.h new file mode 100644 index 0000000000..f06ec814b4 --- /dev/null +++ b/ext/json/vendor/ryu.h @@ -0,0 +1,819 @@ +// Copyright 2018 Ulf Adams +// +// The contents of this file may be used under the terms of the Apache License, +// Version 2.0. +// +// Alternatively, the contents of this file may be used under the terms of +// the Boost Software License, Version 1.0. +// +// Unless required by applicable law or agreed to in writing, this software +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. +// +// --- +// +// Apache License +// Version 2.0, January 2004 +// http://www.apache.org/licenses/ +// +// TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +// +// 1. Definitions. +// +// "License" shall mean the terms and conditions for use, reproduction, +// and distribution as defined by Sections 1 through 9 of this document. +// +// "Licensor" shall mean the copyright owner or entity authorized by +// the copyright owner that is granting the License. +// +// "Legal Entity" shall mean the union of the acting entity and all +// other entities that control, are controlled by, or are under common +// control with that entity. For the purposes of this definition, +// "control" means (i) the power, direct or indirect, to cause the +// direction or management of such entity, whether by contract or +// otherwise, or (ii) ownership of fifty percent (50%) or more of the +// outstanding shares, or (iii) beneficial ownership of such entity. +// +// "You" (or "Your") shall mean an individual or Legal Entity +// exercising permissions granted by this License. +// +// "Source" form shall mean the preferred form for making modifications, +// including but not limited to software source code, documentation +// source, and configuration files. +// +// "Object" form shall mean any form resulting from mechanical +// transformation or translation of a Source form, including but +// not limited to compiled object code, generated documentation, +// and conversions to other media types. +// +// "Work" shall mean the work of authorship, whether in Source or +// Object form, made available under the License, as indicated by a +// copyright notice that is included in or attached to the work +// (an example is provided in the Appendix below). +// +// "Derivative Works" shall mean any work, whether in Source or Object +// form, that is based on (or derived from) the Work and for which the +// editorial revisions, annotations, elaborations, or other modifications +// represent, as a whole, an original work of authorship. For the purposes +// of this License, Derivative Works shall not include works that remain +// separable from, or merely link (or bind by name) to the interfaces of, +// the Work and Derivative Works thereof. +// +// "Contribution" shall mean any work of authorship, including +// the original version of the Work and any modifications or additions +// to that Work or Derivative Works thereof, that is intentionally +// submitted to Licensor for inclusion in the Work by the copyright owner +// or by an individual or Legal Entity authorized to submit on behalf of +// the copyright owner. For the purposes of this definition, "submitted" +// means any form of electronic, verbal, or written communication sent +// to the Licensor or its representatives, including but not limited to +// communication on electronic mailing lists, source code control systems, +// and issue tracking systems that are managed by, or on behalf of, the +// Licensor for the purpose of discussing and improving the Work, but +// excluding communication that is conspicuously marked or otherwise +// designated in writing by the copyright owner as "Not a Contribution." +// +// "Contributor" shall mean Licensor and any individual or Legal Entity +// on behalf of whom a Contribution has been received by Licensor and +// subsequently incorporated within the Work. +// +// 2. Grant of Copyright License. Subject to the terms and conditions of +// this License, each Contributor hereby grants to You a perpetual, +// worldwide, non-exclusive, no-charge, royalty-free, irrevocable +// copyright license to reproduce, prepare Derivative Works of, +// publicly display, publicly perform, sublicense, and distribute the +// Work and such Derivative Works in Source or Object form. +// +// 3. Grant of Patent License. Subject to the terms and conditions of +// this License, each Contributor hereby grants to You a perpetual, +// worldwide, non-exclusive, no-charge, royalty-free, irrevocable +// (except as stated in this section) patent license to make, have made, +// use, offer to sell, sell, import, and otherwise transfer the Work, +// where such license applies only to those patent claims licensable +// by such Contributor that are necessarily infringed by their +// Contribution(s) alone or by combination of their Contribution(s) +// with the Work to which such Contribution(s) was submitted. If You +// institute patent litigation against any entity (including a +// cross-claim or counterclaim in a lawsuit) alleging that the Work +// or a Contribution incorporated within the Work constitutes direct +// or contributory patent infringement, then any patent licenses +// granted to You under this License for that Work shall terminate +// as of the date such litigation is filed. +// +// 4. Redistribution. You may reproduce and distribute copies of the +// Work or Derivative Works thereof in any medium, with or without +// modifications, and in Source or Object form, provided that You +// meet the following conditions: +// +// (a) You must give any other recipients of the Work or +// Derivative Works a copy of this License; and +// +// (b) You must cause any modified files to carry prominent notices +// stating that You changed the files; and +// +// (c) You must retain, in the Source form of any Derivative Works +// that You distribute, all copyright, patent, trademark, and +// attribution notices from the Source form of the Work, +// excluding those notices that do not pertain to any part of +// the Derivative Works; and +// +// (d) If the Work includes a "NOTICE" text file as part of its +// distribution, then any Derivative Works that You distribute must +// include a readable copy of the attribution notices contained +// within such NOTICE file, excluding those notices that do not +// pertain to any part of the Derivative Works, in at least one +// of the following places: within a NOTICE text file distributed +// as part of the Derivative Works; within the Source form or +// documentation, if provided along with the Derivative Works; or, +// within a display generated by the Derivative Works, if and +// wherever such third-party notices normally appear. The contents +// of the NOTICE file are for informational purposes only and +// do not modify the License. You may add Your own attribution +// notices within Derivative Works that You distribute, alongside +// or as an addendum to the NOTICE text from the Work, provided +// that such additional attribution notices cannot be construed +// as modifying the License. +// +// You may add Your own copyright statement to Your modifications and +// may provide additional or different license terms and conditions +// for use, reproduction, or distribution of Your modifications, or +// for any such Derivative Works as a whole, provided Your use, +// reproduction, and distribution of the Work otherwise complies with +// the conditions stated in this License. +// +// 5. Submission of Contributions. Unless You explicitly state otherwise, +// any Contribution intentionally submitted for inclusion in the Work +// by You to the Licensor shall be under the terms and conditions of +// this License, without any additional terms or conditions. +// Notwithstanding the above, nothing herein shall supersede or modify +// the terms of any separate license agreement you may have executed +// with Licensor regarding such Contributions. +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor, +// except as required for reasonable and customary use in describing the +// origin of the Work and reproducing the content of the NOTICE file. +// +// 7. Disclaimer of Warranty. Unless required by applicable law or +// agreed to in writing, Licensor provides the Work (and each +// Contributor provides its Contributions) on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied, including, without limitation, any warranties or conditions +// of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +// PARTICULAR PURPOSE. You are solely responsible for determining the +// appropriateness of using or redistributing the Work and assume any +// risks associated with Your exercise of permissions under this License. +// +// 8. Limitation of Liability. In no event and under no legal theory, +// whether in tort (including negligence), contract, or otherwise, +// unless required by applicable law (such as deliberate and grossly +// negligent acts) or agreed to in writing, shall any Contributor be +// liable to You for damages, including any direct, indirect, special, +// incidental, or consequential damages of any character arising as a +// result of this License or out of the use or inability to use the +// Work (including but not limited to damages for loss of goodwill, +// work stoppage, computer failure or malfunction, or any and all +// other commercial damages or losses), even if such Contributor +// has been advised of the possibility of such damages. +// +// 9. Accepting Warranty or Additional Liability. While redistributing +// the Work or Derivative Works thereof, You may choose to offer, +// and charge a fee for, acceptance of support, warranty, indemnity, +// or other liability obligations and/or rights consistent with this +// License. However, in accepting such obligations, You may act only +// on Your own behalf and on Your sole responsibility, not on behalf +// of any other Contributor, and only if You agree to indemnify, +// defend, and hold each Contributor harmless for any liability +// incurred by, or claims asserted against, such Contributor by reason +// of your accepting any such warranty or additional liability. +// +// END OF TERMS AND CONDITIONS +// +// APPENDIX: How to apply the Apache License to your work. +// +// To apply the Apache License to your work, attach the following +// boilerplate notice, with the fields enclosed by brackets "[]" +// replaced with your own identifying information. (Don't include +// the brackets!) The text should be enclosed in the appropriate +// comment syntax for the file format. We also recommend that a +// file or class name and description of purpose be included on the +// same "printed page" as the copyright notice for easier +// identification within third-party archives. +// +// Copyright [yyyy] [name of copyright owner] +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// --- +// +// Boost Software License - Version 1.0 - August 17th, 2003 +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// --- +// Minimal Ryu implementation adapted for Ruby JSON gem by Josef Šimánek +// Optimized for pre-extracted mantissa/exponent from JSON parsing +// This is a stripped-down version containing only what's needed for +// converting decimal mantissa+exponent to IEEE 754 double precision. + +#ifndef RYU_H +#define RYU_H + +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +// Detect __builtin_clzll availability (for floor_log2) +// Note: MSVC doesn't have __builtin_clzll, so we provide a fallback +#ifdef __clang__ + #if __has_builtin(__builtin_clzll) + #define RYU_HAVE_BUILTIN_CLZLL 1 + #else + #define RYU_HAVE_BUILTIN_CLZLL 0 + #endif +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + #define RYU_HAVE_BUILTIN_CLZLL 1 +#else + #define RYU_HAVE_BUILTIN_CLZLL 0 +#endif + +// Count leading zeros (for floor_log2) +static inline uint32_t ryu_leading_zeros64(uint64_t input) +{ +#if RYU_HAVE_BUILTIN_CLZLL + return __builtin_clzll(input); +#else + // Fallback: binary search for the highest set bit + // This works on MSVC and other compilers without __builtin_clzll + if (input == 0) return 64; + uint32_t n = 0; + if (input <= 0x00000000FFFFFFFFULL) { n += 32; input <<= 32; } + if (input <= 0x0000FFFFFFFFFFFFULL) { n += 16; input <<= 16; } + if (input <= 0x00FFFFFFFFFFFFFFULL) { n += 8; input <<= 8; } + if (input <= 0x0FFFFFFFFFFFFFFFULL) { n += 4; input <<= 4; } + if (input <= 0x3FFFFFFFFFFFFFFFULL) { n += 2; input <<= 2; } + if (input <= 0x7FFFFFFFFFFFFFFFULL) { n += 1; } + return n; +#endif +} + +// These tables are generated by PrintDoubleLookupTable. +#define DOUBLE_POW5_INV_BITCOUNT 125 +#define DOUBLE_POW5_BITCOUNT 125 + +#define DOUBLE_POW5_INV_TABLE_SIZE 342 +#define DOUBLE_POW5_TABLE_SIZE 326 + +static const uint64_t DOUBLE_POW5_INV_SPLIT[DOUBLE_POW5_INV_TABLE_SIZE][2] = { + { 1u, 2305843009213693952u }, { 11068046444225730970u, 1844674407370955161u }, + { 5165088340638674453u, 1475739525896764129u }, { 7821419487252849886u, 1180591620717411303u }, + { 8824922364862649494u, 1888946593147858085u }, { 7059937891890119595u, 1511157274518286468u }, + { 13026647942995916322u, 1208925819614629174u }, { 9774590264567735146u, 1934281311383406679u }, + { 11509021026396098440u, 1547425049106725343u }, { 16585914450600699399u, 1237940039285380274u }, + { 15469416676735388068u, 1980704062856608439u }, { 16064882156130220778u, 1584563250285286751u }, + { 9162556910162266299u, 1267650600228229401u }, { 7281393426775805432u, 2028240960365167042u }, + { 16893161185646375315u, 1622592768292133633u }, { 2446482504291369283u, 1298074214633706907u }, + { 7603720821608101175u, 2076918743413931051u }, { 2393627842544570617u, 1661534994731144841u }, + { 16672297533003297786u, 1329227995784915872u }, { 11918280793837635165u, 2126764793255865396u }, + { 5845275820328197809u, 1701411834604692317u }, { 15744267100488289217u, 1361129467683753853u }, + { 3054734472329800808u, 2177807148294006166u }, { 17201182836831481939u, 1742245718635204932u }, + { 6382248639981364905u, 1393796574908163946u }, { 2832900194486363201u, 2230074519853062314u }, + { 5955668970331000884u, 1784059615882449851u }, { 1075186361522890384u, 1427247692705959881u }, + { 12788344622662355584u, 2283596308329535809u }, { 13920024512871794791u, 1826877046663628647u }, + { 3757321980813615186u, 1461501637330902918u }, { 10384555214134712795u, 1169201309864722334u }, + { 5547241898389809503u, 1870722095783555735u }, { 4437793518711847602u, 1496577676626844588u }, + { 10928932444453298728u, 1197262141301475670u }, { 17486291911125277965u, 1915619426082361072u }, + { 6610335899416401726u, 1532495540865888858u }, { 12666966349016942027u, 1225996432692711086u }, + { 12888448528943286597u, 1961594292308337738u }, { 17689456452638449924u, 1569275433846670190u }, + { 14151565162110759939u, 1255420347077336152u }, { 7885109000409574610u, 2008672555323737844u }, + { 9997436015069570011u, 1606938044258990275u }, { 7997948812055656009u, 1285550435407192220u }, + { 12796718099289049614u, 2056880696651507552u }, { 2858676849947419045u, 1645504557321206042u }, + { 13354987924183666206u, 1316403645856964833u }, { 17678631863951955605u, 2106245833371143733u }, + { 3074859046935833515u, 1684996666696914987u }, { 13527933681774397782u, 1347997333357531989u }, + { 10576647446613305481u, 2156795733372051183u }, { 15840015586774465031u, 1725436586697640946u }, + { 8982663654677661702u, 1380349269358112757u }, { 18061610662226169046u, 2208558830972980411u }, + { 10759939715039024913u, 1766847064778384329u }, { 12297300586773130254u, 1413477651822707463u }, + { 15986332124095098083u, 2261564242916331941u }, { 9099716884534168143u, 1809251394333065553u }, + { 14658471137111155161u, 1447401115466452442u }, { 4348079280205103483u, 1157920892373161954u }, + { 14335624477811986218u, 1852673427797059126u }, { 7779150767507678651u, 1482138742237647301u }, + { 2533971799264232598u, 1185710993790117841u }, { 15122401323048503126u, 1897137590064188545u }, + { 12097921058438802501u, 1517710072051350836u }, { 5988988032009131678u, 1214168057641080669u }, + { 16961078480698431330u, 1942668892225729070u }, { 13568862784558745064u, 1554135113780583256u }, + { 7165741412905085728u, 1243308091024466605u }, { 11465186260648137165u, 1989292945639146568u }, + { 16550846638002330379u, 1591434356511317254u }, { 16930026125143774626u, 1273147485209053803u }, + { 4951948911778577463u, 2037035976334486086u }, { 272210314680951647u, 1629628781067588869u }, + { 3907117066486671641u, 1303703024854071095u }, { 6251387306378674625u, 2085924839766513752u }, + { 16069156289328670670u, 1668739871813211001u }, { 9165976216721026213u, 1334991897450568801u }, + { 7286864317269821294u, 2135987035920910082u }, { 16897537898041588005u, 1708789628736728065u }, + { 13518030318433270404u, 1367031702989382452u }, { 6871453250525591353u, 2187250724783011924u }, + { 9186511415162383406u, 1749800579826409539u }, { 11038557946871817048u, 1399840463861127631u }, + { 10282995085511086630u, 2239744742177804210u }, { 8226396068408869304u, 1791795793742243368u }, + { 13959814484210916090u, 1433436634993794694u }, { 11267656730511734774u, 2293498615990071511u }, + { 5324776569667477496u, 1834798892792057209u }, { 7949170070475892320u, 1467839114233645767u }, + { 17427382500606444826u, 1174271291386916613u }, { 5747719112518849781u, 1878834066219066582u }, + { 15666221734240810795u, 1503067252975253265u }, { 12532977387392648636u, 1202453802380202612u }, + { 5295368560860596524u, 1923926083808324180u }, { 4236294848688477220u, 1539140867046659344u }, + { 7078384693692692099u, 1231312693637327475u }, { 11325415509908307358u, 1970100309819723960u }, + { 9060332407926645887u, 1576080247855779168u }, { 14626963555825137356u, 1260864198284623334u }, + { 12335095245094488799u, 2017382717255397335u }, { 9868076196075591040u, 1613906173804317868u }, + { 15273158586344293478u, 1291124939043454294u }, { 13369007293925138595u, 2065799902469526871u }, + { 7005857020398200553u, 1652639921975621497u }, { 16672732060544291412u, 1322111937580497197u }, + { 11918976037903224966u, 2115379100128795516u }, { 5845832015580669650u, 1692303280103036413u }, + { 12055363241948356366u, 1353842624082429130u }, { 841837113407818570u, 2166148198531886609u }, + { 4362818505468165179u, 1732918558825509287u }, { 14558301248600263113u, 1386334847060407429u }, + { 12225235553534690011u, 2218135755296651887u }, { 2401490813343931363u, 1774508604237321510u }, + { 1921192650675145090u, 1419606883389857208u }, { 17831303500047873437u, 2271371013423771532u }, + { 6886345170554478103u, 1817096810739017226u }, { 1819727321701672159u, 1453677448591213781u }, + { 16213177116328979020u, 1162941958872971024u }, { 14873036941900635463u, 1860707134196753639u }, + { 15587778368262418694u, 1488565707357402911u }, { 8780873879868024632u, 1190852565885922329u }, + { 2981351763563108441u, 1905364105417475727u }, { 13453127855076217722u, 1524291284333980581u }, + { 7073153469319063855u, 1219433027467184465u }, { 11317045550910502167u, 1951092843947495144u }, + { 12742985255470312057u, 1560874275157996115u }, { 10194388204376249646u, 1248699420126396892u }, + { 1553625868034358140u, 1997919072202235028u }, { 8621598323911307159u, 1598335257761788022u }, + { 17965325103354776697u, 1278668206209430417u }, { 13987124906400001422u, 2045869129935088668u }, + { 121653480894270168u, 1636695303948070935u }, { 97322784715416134u, 1309356243158456748u }, + { 14913111714512307107u, 2094969989053530796u }, { 8241140556867935363u, 1675975991242824637u }, + { 17660958889720079260u, 1340780792994259709u }, { 17189487779326395846u, 2145249268790815535u }, + { 13751590223461116677u, 1716199415032652428u }, { 18379969808252713988u, 1372959532026121942u }, + { 14650556434236701088u, 2196735251241795108u }, { 652398703163629901u, 1757388200993436087u }, + { 11589965406756634890u, 1405910560794748869u }, { 7475898206584884855u, 2249456897271598191u }, + { 2291369750525997561u, 1799565517817278553u }, { 9211793429904618695u, 1439652414253822842u }, + { 18428218302589300235u, 2303443862806116547u }, { 7363877012587619542u, 1842755090244893238u }, + { 13269799239553916280u, 1474204072195914590u }, { 10615839391643133024u, 1179363257756731672u }, + { 2227947767661371545u, 1886981212410770676u }, { 16539753473096738529u, 1509584969928616540u }, + { 13231802778477390823u, 1207667975942893232u }, { 6413489186596184024u, 1932268761508629172u }, + { 16198837793502678189u, 1545815009206903337u }, { 5580372605318321905u, 1236652007365522670u }, + { 8928596168509315048u, 1978643211784836272u }, { 18210923379033183008u, 1582914569427869017u }, + { 7190041073742725760u, 1266331655542295214u }, { 436019273762630246u, 2026130648867672343u }, + { 7727513048493924843u, 1620904519094137874u }, { 9871359253537050198u, 1296723615275310299u }, + { 4726128361433549347u, 2074757784440496479u }, { 7470251503888749801u, 1659806227552397183u }, + { 13354898832594820487u, 1327844982041917746u }, { 13989140502667892133u, 2124551971267068394u }, + { 14880661216876224029u, 1699641577013654715u }, { 11904528973500979224u, 1359713261610923772u }, + { 4289851098633925465u, 2175541218577478036u }, { 18189276137874781665u, 1740432974861982428u }, + { 3483374466074094362u, 1392346379889585943u }, { 1884050330976640656u, 2227754207823337509u }, + { 5196589079523222848u, 1782203366258670007u }, { 15225317707844309248u, 1425762693006936005u }, + { 5913764258841343181u, 2281220308811097609u }, { 8420360221814984868u, 1824976247048878087u }, + { 17804334621677718864u, 1459980997639102469u }, { 17932816512084085415u, 1167984798111281975u }, + { 10245762345624985047u, 1868775676978051161u }, { 4507261061758077715u, 1495020541582440929u }, + { 7295157664148372495u, 1196016433265952743u }, { 7982903447895485668u, 1913626293225524389u }, + { 10075671573058298858u, 1530901034580419511u }, { 4371188443704728763u, 1224720827664335609u }, + { 14372599139411386667u, 1959553324262936974u }, { 15187428126271019657u, 1567642659410349579u }, + { 15839291315758726049u, 1254114127528279663u }, { 3206773216762499739u, 2006582604045247462u }, + { 13633465017635730761u, 1605266083236197969u }, { 14596120828850494932u, 1284212866588958375u }, + { 4907049252451240275u, 2054740586542333401u }, { 236290587219081897u, 1643792469233866721u }, + { 14946427728742906810u, 1315033975387093376u }, { 16535586736504830250u, 2104054360619349402u }, + { 5849771759720043554u, 1683243488495479522u }, { 15747863852001765813u, 1346594790796383617u }, + { 10439186904235184007u, 2154551665274213788u }, { 15730047152871967852u, 1723641332219371030u }, + { 12584037722297574282u, 1378913065775496824u }, { 9066413911450387881u, 2206260905240794919u }, + { 10942479943902220628u, 1765008724192635935u }, { 8753983955121776503u, 1412006979354108748u }, + { 10317025513452932081u, 2259211166966573997u }, { 874922781278525018u, 1807368933573259198u }, + { 8078635854506640661u, 1445895146858607358u }, { 13841606313089133175u, 1156716117486885886u }, + { 14767872471458792434u, 1850745787979017418u }, { 746251532941302978u, 1480596630383213935u }, + { 597001226353042382u, 1184477304306571148u }, { 15712597221132509104u, 1895163686890513836u }, + { 8880728962164096960u, 1516130949512411069u }, { 10793931984473187891u, 1212904759609928855u }, + { 17270291175157100626u, 1940647615375886168u }, { 2748186495899949531u, 1552518092300708935u }, + { 2198549196719959625u, 1242014473840567148u }, { 18275073973719576693u, 1987223158144907436u }, + { 10930710364233751031u, 1589778526515925949u }, { 12433917106128911148u, 1271822821212740759u }, + { 8826220925580526867u, 2034916513940385215u }, { 7060976740464421494u, 1627933211152308172u }, + { 16716827836597268165u, 1302346568921846537u }, { 11989529279587987770u, 2083754510274954460u }, + { 9591623423670390216u, 1667003608219963568u }, { 15051996368420132820u, 1333602886575970854u }, + { 13015147745246481542u, 2133764618521553367u }, { 3033420566713364587u, 1707011694817242694u }, + { 6116085268112601993u, 1365609355853794155u }, { 9785736428980163188u, 2184974969366070648u }, + { 15207286772667951197u, 1747979975492856518u }, { 1097782973908629988u, 1398383980394285215u }, + { 1756452758253807981u, 2237414368630856344u }, { 5094511021344956708u, 1789931494904685075u }, + { 4075608817075965366u, 1431945195923748060u }, { 6520974107321544586u, 2291112313477996896u }, + { 1527430471115325346u, 1832889850782397517u }, { 12289990821117991246u, 1466311880625918013u }, + { 17210690286378213644u, 1173049504500734410u }, { 9090360384495590213u, 1876879207201175057u }, + { 18340334751822203140u, 1501503365760940045u }, { 14672267801457762512u, 1201202692608752036u }, + { 16096930852848599373u, 1921924308174003258u }, { 1809498238053148529u, 1537539446539202607u }, + { 12515645034668249793u, 1230031557231362085u }, { 1578287981759648052u, 1968050491570179337u }, + { 12330676829633449412u, 1574440393256143469u }, { 13553890278448669853u, 1259552314604914775u }, + { 3239480371808320148u, 2015283703367863641u }, { 17348979556414297411u, 1612226962694290912u }, + { 6500486015647617283u, 1289781570155432730u }, { 10400777625036187652u, 2063650512248692368u }, + { 15699319729512770768u, 1650920409798953894u }, { 16248804598352126938u, 1320736327839163115u }, + { 7551343283653851484u, 2113178124542660985u }, { 6041074626923081187u, 1690542499634128788u }, + { 12211557331022285596u, 1352433999707303030u }, { 1091747655926105338u, 2163894399531684849u }, + { 4562746939482794594u, 1731115519625347879u }, { 7339546366328145998u, 1384892415700278303u }, + { 8053925371383123274u, 2215827865120445285u }, { 6443140297106498619u, 1772662292096356228u }, + { 12533209867169019542u, 1418129833677084982u }, { 5295740528502789974u, 2269007733883335972u }, + { 15304638867027962949u, 1815206187106668777u }, { 4865013464138549713u, 1452164949685335022u }, + { 14960057215536570740u, 1161731959748268017u }, { 9178696285890871890u, 1858771135597228828u }, + { 14721654658196518159u, 1487016908477783062u }, { 4398626097073393881u, 1189613526782226450u }, + { 7037801755317430209u, 1903381642851562320u }, { 5630241404253944167u, 1522705314281249856u }, + { 814844308661245011u, 1218164251424999885u }, { 1303750893857992017u, 1949062802279999816u }, + { 15800395974054034906u, 1559250241823999852u }, { 5261619149759407279u, 1247400193459199882u }, + { 12107939454356961969u, 1995840309534719811u }, { 5997002748743659252u, 1596672247627775849u }, + { 8486951013736837725u, 1277337798102220679u }, { 2511075177753209390u, 2043740476963553087u }, + { 13076906586428298482u, 1634992381570842469u }, { 14150874083884549109u, 1307993905256673975u }, + { 4194654460505726958u, 2092790248410678361u }, { 18113118827372222859u, 1674232198728542688u }, + { 3422448617672047318u, 1339385758982834151u }, { 16543964232501006678u, 2143017214372534641u }, + { 9545822571258895019u, 1714413771498027713u }, { 15015355686490936662u, 1371531017198422170u }, + { 5577825024675947042u, 2194449627517475473u }, { 11840957649224578280u, 1755559702013980378u }, + { 16851463748863483271u, 1404447761611184302u }, { 12204946739213931940u, 2247116418577894884u }, + { 13453306206113055875u, 1797693134862315907u }, { 3383947335406624054u, 1438154507889852726u }, + { 16482362180876329456u, 2301047212623764361u }, { 9496540929959153242u, 1840837770099011489u }, + { 11286581558709232917u, 1472670216079209191u }, { 5339916432225476010u, 1178136172863367353u }, + { 4854517476818851293u, 1885017876581387765u }, { 3883613981455081034u, 1508014301265110212u }, + { 14174937629389795797u, 1206411441012088169u }, { 11611853762797942306u, 1930258305619341071u }, + { 5600134195496443521u, 1544206644495472857u }, { 15548153800622885787u, 1235365315596378285u }, + { 6430302007287065643u, 1976584504954205257u }, { 16212288050055383484u, 1581267603963364205u }, + { 12969830440044306787u, 1265014083170691364u }, { 9683682259845159889u, 2024022533073106183u }, + { 15125643437359948558u, 1619218026458484946u }, { 8411165935146048523u, 1295374421166787957u }, + { 17147214310975587960u, 2072599073866860731u }, { 10028422634038560045u, 1658079259093488585u }, + { 8022738107230848036u, 1326463407274790868u }, { 9147032156827446534u, 2122341451639665389u }, + { 11006974540203867551u, 1697873161311732311u }, { 5116230817421183718u, 1358298529049385849u }, + { 15564666937357714594u, 2173277646479017358u }, { 1383687105660440706u, 1738622117183213887u }, + { 12174996128754083534u, 1390897693746571109u }, { 8411947361780802685u, 2225436309994513775u }, + { 6729557889424642148u, 1780349047995611020u }, { 5383646311539713719u, 1424279238396488816u }, + { 1235136468979721303u, 2278846781434382106u }, { 15745504434151418335u, 1823077425147505684u }, + { 16285752362063044992u, 1458461940118004547u }, { 5649904260166615347u, 1166769552094403638u }, + { 5350498001524674232u, 1866831283351045821u }, { 591049586477829062u, 1493465026680836657u }, + { 11540886113407994219u, 1194772021344669325u }, { 18673707743239135u, 1911635234151470921u }, + { 14772334225162232601u, 1529308187321176736u }, { 8128518565387875758u, 1223446549856941389u }, + { 1937583260394870242u, 1957514479771106223u }, { 8928764237799716840u, 1566011583816884978u }, + { 14521709019723594119u, 1252809267053507982u }, { 8477339172590109297u, 2004494827285612772u }, + { 17849917782297818407u, 1603595861828490217u }, { 6901236596354434079u, 1282876689462792174u }, + { 18420676183650915173u, 2052602703140467478u }, { 3668494502695001169u, 1642082162512373983u }, + { 10313493231639821582u, 1313665730009899186u }, { 9122891541139893884u, 2101865168015838698u }, + { 14677010862395735754u, 1681492134412670958u }, { 673562245690857633u, 1345193707530136767u } +}; + +static const uint64_t DOUBLE_POW5_SPLIT[DOUBLE_POW5_TABLE_SIZE][2] = { + { 0u, 1152921504606846976u }, { 0u, 1441151880758558720u }, + { 0u, 1801439850948198400u }, { 0u, 2251799813685248000u }, + { 0u, 1407374883553280000u }, { 0u, 1759218604441600000u }, + { 0u, 2199023255552000000u }, { 0u, 1374389534720000000u }, + { 0u, 1717986918400000000u }, { 0u, 2147483648000000000u }, + { 0u, 1342177280000000000u }, { 0u, 1677721600000000000u }, + { 0u, 2097152000000000000u }, { 0u, 1310720000000000000u }, + { 0u, 1638400000000000000u }, { 0u, 2048000000000000000u }, + { 0u, 1280000000000000000u }, { 0u, 1600000000000000000u }, + { 0u, 2000000000000000000u }, { 0u, 1250000000000000000u }, + { 0u, 1562500000000000000u }, { 0u, 1953125000000000000u }, + { 0u, 1220703125000000000u }, { 0u, 1525878906250000000u }, + { 0u, 1907348632812500000u }, { 0u, 1192092895507812500u }, + { 0u, 1490116119384765625u }, { 4611686018427387904u, 1862645149230957031u }, + { 9799832789158199296u, 1164153218269348144u }, { 12249790986447749120u, 1455191522836685180u }, + { 15312238733059686400u, 1818989403545856475u }, { 14528612397897220096u, 2273736754432320594u }, + { 13692068767113150464u, 1421085471520200371u }, { 12503399940464050176u, 1776356839400250464u }, + { 15629249925580062720u, 2220446049250313080u }, { 9768281203487539200u, 1387778780781445675u }, + { 7598665485932036096u, 1734723475976807094u }, { 274959820560269312u, 2168404344971008868u }, + { 9395221924704944128u, 1355252715606880542u }, { 2520655369026404352u, 1694065894508600678u }, + { 12374191248137781248u, 2117582368135750847u }, { 14651398557727195136u, 1323488980084844279u }, + { 13702562178731606016u, 1654361225106055349u }, { 3293144668132343808u, 2067951531382569187u }, + { 18199116482078572544u, 1292469707114105741u }, { 8913837547316051968u, 1615587133892632177u }, + { 15753982952572452864u, 2019483917365790221u }, { 12152082354571476992u, 1262177448353618888u }, + { 15190102943214346240u, 1577721810442023610u }, { 9764256642163156992u, 1972152263052529513u }, + { 17631875447420442880u, 1232595164407830945u }, { 8204786253993389888u, 1540743955509788682u }, + { 1032610780636961552u, 1925929944387235853u }, { 2951224747111794922u, 1203706215242022408u }, + { 3689030933889743652u, 1504632769052528010u }, { 13834660704216955373u, 1880790961315660012u }, + { 17870034976990372916u, 1175494350822287507u }, { 17725857702810578241u, 1469367938527859384u }, + { 3710578054803671186u, 1836709923159824231u }, { 26536550077201078u, 2295887403949780289u }, + { 11545800389866720434u, 1434929627468612680u }, { 14432250487333400542u, 1793662034335765850u }, + { 8816941072311974870u, 2242077542919707313u }, { 17039803216263454053u, 1401298464324817070u }, + { 12076381983474541759u, 1751623080406021338u }, { 5872105442488401391u, 2189528850507526673u }, + { 15199280947623720629u, 1368455531567204170u }, { 9775729147674874978u, 1710569414459005213u }, + { 16831347453020981627u, 2138211768073756516u }, { 1296220121283337709u, 1336382355046097823u }, + { 15455333206886335848u, 1670477943807622278u }, { 10095794471753144002u, 2088097429759527848u }, + { 6309871544845715001u, 1305060893599704905u }, { 12499025449484531656u, 1631326116999631131u }, + { 11012095793428276666u, 2039157646249538914u }, { 11494245889320060820u, 1274473528905961821u }, + { 532749306367912313u, 1593091911132452277u }, { 5277622651387278295u, 1991364888915565346u }, + { 7910200175544436838u, 1244603055572228341u }, { 14499436237857933952u, 1555753819465285426u }, + { 8900923260467641632u, 1944692274331606783u }, { 12480606065433357876u, 1215432671457254239u }, + { 10989071563364309441u, 1519290839321567799u }, { 9124653435777998898u, 1899113549151959749u }, + { 8008751406574943263u, 1186945968219974843u }, { 5399253239791291175u, 1483682460274968554u }, + { 15972438586593889776u, 1854603075343710692u }, { 759402079766405302u, 1159126922089819183u }, + { 14784310654990170340u, 1448908652612273978u }, { 9257016281882937117u, 1811135815765342473u }, + { 16182956370781059300u, 2263919769706678091u }, { 7808504722524468110u, 1414949856066673807u }, + { 5148944884728197234u, 1768687320083342259u }, { 1824495087482858639u, 2210859150104177824u }, + { 1140309429676786649u, 1381786968815111140u }, { 1425386787095983311u, 1727233711018888925u }, + { 6393419502297367043u, 2159042138773611156u }, { 13219259225790630210u, 1349401336733506972u }, + { 16524074032238287762u, 1686751670916883715u }, { 16043406521870471799u, 2108439588646104644u }, + { 803757039314269066u, 1317774742903815403u }, { 14839754354425000045u, 1647218428629769253u }, + { 4714634887749086344u, 2059023035787211567u }, { 9864175832484260821u, 1286889397367007229u }, + { 16941905809032713930u, 1608611746708759036u }, { 2730638187581340797u, 2010764683385948796u }, + { 10930020904093113806u, 1256727927116217997u }, { 18274212148543780162u, 1570909908895272496u }, + { 4396021111970173586u, 1963637386119090621u }, { 5053356204195052443u, 1227273366324431638u }, + { 15540067292098591362u, 1534091707905539547u }, { 14813398096695851299u, 1917614634881924434u }, + { 13870059828862294966u, 1198509146801202771u }, { 12725888767650480803u, 1498136433501503464u }, + { 15907360959563101004u, 1872670541876879330u }, { 14553786618154326031u, 1170419088673049581u }, + { 4357175217410743827u, 1463023860841311977u }, { 10058155040190817688u, 1828779826051639971u }, + { 7961007781811134206u, 2285974782564549964u }, { 14199001900486734687u, 1428734239102843727u }, + { 13137066357181030455u, 1785917798878554659u }, { 11809646928048900164u, 2232397248598193324u }, + { 16604401366885338411u, 1395248280373870827u }, { 16143815690179285109u, 1744060350467338534u }, + { 10956397575869330579u, 2180075438084173168u }, { 6847748484918331612u, 1362547148802608230u }, + { 17783057643002690323u, 1703183936003260287u }, { 17617136035325974999u, 2128979920004075359u }, + { 17928239049719816230u, 1330612450002547099u }, { 17798612793722382384u, 1663265562503183874u }, + { 13024893955298202172u, 2079081953128979843u }, { 5834715712847682405u, 1299426220705612402u }, + { 16516766677914378815u, 1624282775882015502u }, { 11422586310538197711u, 2030353469852519378u }, + { 11750802462513761473u, 1268970918657824611u }, { 10076817059714813937u, 1586213648322280764u }, + { 12596021324643517422u, 1982767060402850955u }, { 5566670318688504437u, 1239229412751781847u }, + { 2346651879933242642u, 1549036765939727309u }, { 7545000868343941206u, 1936295957424659136u }, + { 4715625542714963254u, 1210184973390411960u }, { 5894531928393704067u, 1512731216738014950u }, + { 16591536947346905892u, 1890914020922518687u }, { 17287239619732898039u, 1181821263076574179u }, + { 16997363506238734644u, 1477276578845717724u }, { 2799960309088866689u, 1846595723557147156u }, + { 10973347230035317489u, 1154122327223216972u }, { 13716684037544146861u, 1442652909029021215u }, + { 12534169028502795672u, 1803316136286276519u }, { 11056025267201106687u, 2254145170357845649u }, + { 18439230838069161439u, 1408840731473653530u }, { 13825666510731675991u, 1761050914342066913u }, + { 3447025083132431277u, 2201313642927583642u }, { 6766076695385157452u, 1375821026829739776u }, + { 8457595869231446815u, 1719776283537174720u }, { 10571994836539308519u, 2149720354421468400u }, + { 6607496772837067824u, 1343575221513417750u }, { 17482743002901110588u, 1679469026891772187u }, + { 17241742735199000331u, 2099336283614715234u }, { 15387775227926763111u, 1312085177259197021u }, + { 5399660979626290177u, 1640106471573996277u }, { 11361262242960250625u, 2050133089467495346u }, + { 11712474920277544544u, 1281333180917184591u }, { 10028907631919542777u, 1601666476146480739u }, + { 7924448521472040567u, 2002083095183100924u }, { 14176152362774801162u, 1251301934489438077u }, + { 3885132398186337741u, 1564127418111797597u }, { 9468101516160310080u, 1955159272639746996u }, + { 15140935484454969608u, 1221974545399841872u }, { 479425281859160394u, 1527468181749802341u }, + { 5210967620751338397u, 1909335227187252926u }, { 17091912818251750210u, 1193334516992033078u }, + { 12141518985959911954u, 1491668146240041348u }, { 15176898732449889943u, 1864585182800051685u }, + { 11791404716994875166u, 1165365739250032303u }, { 10127569877816206054u, 1456707174062540379u }, + { 8047776328842869663u, 1820883967578175474u }, { 836348374198811271u, 2276104959472719343u }, + { 7440246761515338900u, 1422565599670449589u }, { 13911994470321561530u, 1778206999588061986u }, + { 8166621051047176104u, 2222758749485077483u }, { 2798295147690791113u, 1389224218428173427u }, + { 17332926989895652603u, 1736530273035216783u }, { 17054472718942177850u, 2170662841294020979u }, + { 8353202440125167204u, 1356664275808763112u }, { 10441503050156459005u, 1695830344760953890u }, + { 3828506775840797949u, 2119787930951192363u }, { 86973725686804766u, 1324867456844495227u }, + { 13943775212390669669u, 1656084321055619033u }, { 3594660960206173375u, 2070105401319523792u }, + { 2246663100128858359u, 1293815875824702370u }, { 12031700912015848757u, 1617269844780877962u }, + { 5816254103165035138u, 2021587305976097453u }, { 5941001823691840913u, 1263492066235060908u }, + { 7426252279614801142u, 1579365082793826135u }, { 4671129331091113523u, 1974206353492282669u }, + { 5225298841145639904u, 1233878970932676668u }, { 6531623551432049880u, 1542348713665845835u }, + { 3552843420862674446u, 1927935892082307294u }, { 16055585193321335241u, 1204959932551442058u }, + { 10846109454796893243u, 1506199915689302573u }, { 18169322836923504458u, 1882749894611628216u }, + { 11355826773077190286u, 1176718684132267635u }, { 9583097447919099954u, 1470898355165334544u }, + { 11978871809898874942u, 1838622943956668180u }, { 14973589762373593678u, 2298278679945835225u }, + { 2440964573842414192u, 1436424174966147016u }, { 3051205717303017741u, 1795530218707683770u }, + { 13037379183483547984u, 2244412773384604712u }, { 8148361989677217490u, 1402757983365377945u }, + { 14797138505523909766u, 1753447479206722431u }, { 13884737113477499304u, 2191809349008403039u }, + { 15595489723564518921u, 1369880843130251899u }, { 14882676136028260747u, 1712351053912814874u }, + { 9379973133180550126u, 2140438817391018593u }, { 17391698254306313589u, 1337774260869386620u }, + { 3292878744173340370u, 1672217826086733276u }, { 4116098430216675462u, 2090272282608416595u }, + { 266718509671728212u, 1306420176630260372u }, { 333398137089660265u, 1633025220787825465u }, + { 5028433689789463235u, 2041281525984781831u }, { 10060300083759496378u, 1275800953740488644u }, + { 12575375104699370472u, 1594751192175610805u }, { 1884160825592049379u, 1993438990219513507u }, + { 17318501580490888525u, 1245899368887195941u }, { 7813068920331446945u, 1557374211108994927u }, + { 5154650131986920777u, 1946717763886243659u }, { 915813323278131534u, 1216698602428902287u }, + { 14979824709379828129u, 1520873253036127858u }, { 9501408849870009354u, 1901091566295159823u }, + { 12855909558809837702u, 1188182228934474889u }, { 2234828893230133415u, 1485227786168093612u }, + { 2793536116537666769u, 1856534732710117015u }, { 8663489100477123587u, 1160334207943823134u }, + { 1605989338741628675u, 1450417759929778918u }, { 11230858710281811652u, 1813022199912223647u }, + { 9426887369424876662u, 2266277749890279559u }, { 12809333633531629769u, 1416423593681424724u }, + { 16011667041914537212u, 1770529492101780905u }, { 6179525747111007803u, 2213161865127226132u }, + { 13085575628799155685u, 1383226165704516332u }, { 16356969535998944606u, 1729032707130645415u }, + { 15834525901571292854u, 2161290883913306769u }, { 2979049660840976177u, 1350806802445816731u }, + { 17558870131333383934u, 1688508503057270913u }, { 8113529608884566205u, 2110635628821588642u }, + { 9682642023980241782u, 1319147268013492901u }, { 16714988548402690132u, 1648934085016866126u }, + { 11670363648648586857u, 2061167606271082658u }, { 11905663298832754689u, 1288229753919426661u }, + { 1047021068258779650u, 1610287192399283327u }, { 15143834390605638274u, 2012858990499104158u }, + { 4853210475701136017u, 1258036869061940099u }, { 1454827076199032118u, 1572546086327425124u }, + { 1818533845248790147u, 1965682607909281405u }, { 3442426662494187794u, 1228551629943300878u }, + { 13526405364972510550u, 1535689537429126097u }, { 3072948650933474476u, 1919611921786407622u }, + { 15755650962115585259u, 1199757451116504763u }, { 15082877684217093670u, 1499696813895630954u }, + { 9630225068416591280u, 1874621017369538693u }, { 8324733676974063502u, 1171638135855961683u }, + { 5794231077790191473u, 1464547669819952104u }, { 7242788847237739342u, 1830684587274940130u }, + { 18276858095901949986u, 2288355734093675162u }, { 16034722328366106645u, 1430222333808546976u }, + { 1596658836748081690u, 1787777917260683721u }, { 6607509564362490017u, 2234722396575854651u }, + { 1823850468512862308u, 1396701497859909157u }, { 6891499104068465790u, 1745876872324886446u }, + { 17837745916940358045u, 2182346090406108057u }, { 4231062170446641922u, 1363966306503817536u }, + { 5288827713058302403u, 1704957883129771920u }, { 6611034641322878003u, 2131197353912214900u }, + { 13355268687681574560u, 1331998346195134312u }, { 16694085859601968200u, 1664997932743917890u }, + { 11644235287647684442u, 2081247415929897363u }, { 4971804045566108824u, 1300779634956185852u }, + { 6214755056957636030u, 1625974543695232315u }, { 3156757802769657134u, 2032468179619040394u }, + { 6584659645158423613u, 1270292612261900246u }, { 17454196593302805324u, 1587865765327375307u }, + { 17206059723201118751u, 1984832206659219134u }, { 6142101308573311315u, 1240520129162011959u }, + { 3065940617289251240u, 1550650161452514949u }, { 8444111790038951954u, 1938312701815643686u }, + { 665883850346957067u, 1211445438634777304u }, { 832354812933696334u, 1514306798293471630u }, + { 10263815553021896226u, 1892883497866839537u }, { 17944099766707154901u, 1183052186166774710u }, + { 13206752671529167818u, 1478815232708468388u }, { 16508440839411459773u, 1848519040885585485u }, + { 12623618533845856310u, 1155324400553490928u }, { 15779523167307320387u, 1444155500691863660u }, + { 1277659885424598868u, 1805194375864829576u }, { 1597074856780748586u, 2256492969831036970u }, + { 5609857803915355770u, 1410308106144398106u }, { 16235694291748970521u, 1762885132680497632u }, + { 1847873790976661535u, 2203606415850622041u }, { 12684136165428883219u, 1377254009906638775u }, + { 11243484188358716120u, 1721567512383298469u }, { 219297180166231438u, 2151959390479123087u }, + { 7054589765244976505u, 1344974619049451929u }, { 13429923224983608535u, 1681218273811814911u }, + { 12175718012802122765u, 2101522842264768639u }, { 14527352785642408584u, 1313451776415480399u }, + { 13547504963625622826u, 1641814720519350499u }, { 12322695186104640628u, 2052268400649188124u }, + { 16925056528170176201u, 1282667750405742577u }, { 7321262604930556539u, 1603334688007178222u }, + { 18374950293017971482u, 2004168360008972777u }, { 4566814905495150320u, 1252605225005607986u }, + { 14931890668723713708u, 1565756531257009982u }, { 9441491299049866327u, 1957195664071262478u }, + { 1289246043478778550u, 1223247290044539049u }, { 6223243572775861092u, 1529059112555673811u }, + { 3167368447542438461u, 1911323890694592264u }, { 1979605279714024038u, 1194577431684120165u }, + { 7086192618069917952u, 1493221789605150206u }, { 18081112809442173248u, 1866527237006437757u }, + { 13606538515115052232u, 1166579523129023598u }, { 7784801107039039482u, 1458224403911279498u }, + { 507629346944023544u, 1822780504889099373u }, { 5246222702107417334u, 2278475631111374216u }, + { 3278889188817135834u, 1424047269444608885u }, { 8710297504448807696u, 1780059086805761106u } +}; + +// IEEE 754 double precision constants +#define DOUBLE_MANTISSA_BITS 52 +#define DOUBLE_EXPONENT_BITS 11 +#define DOUBLE_EXPONENT_BIAS 1023 + +// Helper: floor(log2(value)) using ryu_leading_zeros64 +static inline uint32_t floor_log2(const uint64_t value) { + return 63 - ryu_leading_zeros64(value); +} + +// Helper: log2(5^e) approximation +static inline int32_t log2pow5(const int32_t e) { + return (int32_t) ((((uint32_t) e) * 1217359) >> 19); +} + +// Helper: ceil(log2(5^e)) +static inline int32_t ceil_log2pow5(const int32_t e) { + return log2pow5(e) + 1; +} + +// Helper: max of two int32 +static inline int32_t max32(int32_t a, int32_t b) { + return a < b ? b : a; +} + +// Helper: convert uint64 bits to double +static inline double int64Bits2Double(uint64_t bits) { + double f; + memcpy(&f, &bits, sizeof(double)); + return f; +} + +// Check if value is multiple of 2^p +static inline bool multipleOfPowerOf2(const uint64_t value, const uint32_t p) { + return (value & ((1ull << p) - 1)) == 0; +} + +// Count how many times value is divisible by 5 +// Uses modular inverse to avoid expensive division +static inline uint32_t pow5Factor(uint64_t value) { + const uint64_t m_inv_5 = 14757395258967641293u; // 5 * m_inv_5 = 1 (mod 2^64) + const uint64_t n_div_5 = 3689348814741910323u; // 2^64 / 5 + uint32_t count = 0; + for (;;) { + value *= m_inv_5; + if (value > n_div_5) + break; + ++count; + } + return count; +} + +// Check if value is multiple of 5^p +// Optimized: uses modular inverse instead of division +static inline bool multipleOfPowerOf5(const uint64_t value, const uint32_t p) { + return pow5Factor(value) >= p; +} + +// 128-bit multiplication with shift +// This is the core operation for converting decimal to binary +#if defined(__SIZEOF_INT128__) +// Use native 128-bit integers if available (GCC/Clang) +static inline uint64_t mulShift64(const uint64_t m, const uint64_t* const mul, const int32_t j) { + const unsigned __int128 b0 = ((unsigned __int128) m) * mul[0]; + const unsigned __int128 b2 = ((unsigned __int128) m) * mul[1]; + return (uint64_t) (((b0 >> 64) + b2) >> (j - 64)); +} +#else +// Fallback for systems without 128-bit integers +static inline uint64_t umul128(const uint64_t a, const uint64_t b, uint64_t* const productHi) { + const uint32_t aLo = (uint32_t)a; + const uint32_t aHi = (uint32_t)(a >> 32); + const uint32_t bLo = (uint32_t)b; + const uint32_t bHi = (uint32_t)(b >> 32); + + const uint64_t b00 = (uint64_t)aLo * bLo; + const uint64_t b01 = (uint64_t)aLo * bHi; + const uint64_t b10 = (uint64_t)aHi * bLo; + const uint64_t b11 = (uint64_t)aHi * bHi; + + const uint32_t b00Lo = (uint32_t)b00; + const uint32_t b00Hi = (uint32_t)(b00 >> 32); + + const uint64_t mid1 = b10 + b00Hi; + const uint32_t mid1Lo = (uint32_t)(mid1); + const uint32_t mid1Hi = (uint32_t)(mid1 >> 32); + + const uint64_t mid2 = b01 + mid1Lo; + const uint32_t mid2Lo = (uint32_t)(mid2); + const uint32_t mid2Hi = (uint32_t)(mid2 >> 32); + + const uint64_t pHi = b11 + mid1Hi + mid2Hi; + const uint64_t pLo = ((uint64_t)mid2Lo << 32) | b00Lo; + + *productHi = pHi; + return pLo; +} + +static inline uint64_t shiftright128(const uint64_t lo, const uint64_t hi, const uint32_t dist) { + return (hi << (64 - dist)) | (lo >> dist); +} + +static inline uint64_t mulShift64(const uint64_t m, const uint64_t* const mul, const int32_t j) { + uint64_t high1; + const uint64_t low1 = umul128(m, mul[1], &high1); + uint64_t high0; + umul128(m, mul[0], &high0); + const uint64_t sum = high0 + low1; + if (sum < high0) { + ++high1; + } + return shiftright128(sum, high1, j - 64); +} +#endif + +// Main conversion function: decimal mantissa+exponent to IEEE 754 double +// Optimized for JSON parsing with fast paths for edge cases +static inline double ryu_s2d_from_parts(uint64_t m10, int m10digits, int32_t e10, bool signedM) { + // Fast path: handle zero explicitly (e.g., "0.0", "0e0") + if (m10 == 0) { + return int64Bits2Double(((uint64_t) signedM) << 63); + } + + // Fast path: handle overflow/underflow early + if (m10digits + e10 <= -324) { + // Underflow to zero + return int64Bits2Double(((uint64_t) signedM) << 63); + } + + if (m10digits + e10 >= 310) { + // Overflow to infinity + return int64Bits2Double((((uint64_t) signedM) << 63) | 0x7ff0000000000000ULL); + } + + // Convert decimal to binary: m10 * 10^e10 = m2 * 2^e2 + int32_t e2; + uint64_t m2; + bool trailingZeros; + + if (e10 >= 0) { + // Positive exponent: multiply by 5^e10 and adjust binary exponent + e2 = floor_log2(m10) + e10 + log2pow5(e10) - (DOUBLE_MANTISSA_BITS + 1); + int j = e2 - e10 - ceil_log2pow5(e10) + DOUBLE_POW5_BITCOUNT; + m2 = mulShift64(m10, DOUBLE_POW5_SPLIT[e10], j); + trailingZeros = e2 < e10 || (e2 - e10 < 64 && multipleOfPowerOf2(m10, e2 - e10)); + } else { + // Negative exponent: divide by 5^(-e10) + e2 = floor_log2(m10) + e10 - ceil_log2pow5(-e10) - (DOUBLE_MANTISSA_BITS + 1); + int j = e2 - e10 + ceil_log2pow5(-e10) - 1 + DOUBLE_POW5_INV_BITCOUNT; + m2 = mulShift64(m10, DOUBLE_POW5_INV_SPLIT[-e10], j); + trailingZeros = multipleOfPowerOf5(m10, -e10); + } + + // Compute IEEE 754 exponent + uint32_t ieee_e2 = (uint32_t) max32(0, e2 + DOUBLE_EXPONENT_BIAS + floor_log2(m2)); + + if (ieee_e2 > 0x7fe) { + // Overflow to infinity + return int64Bits2Double((((uint64_t) signedM) << 63) | 0x7ff0000000000000ULL); + } + + // Compute shift amount for rounding + int32_t shift = (ieee_e2 == 0 ? 1 : ieee_e2) - e2 - DOUBLE_EXPONENT_BIAS - DOUBLE_MANTISSA_BITS; + + // IEEE 754 round-to-even (banker's rounding) + trailingZeros &= (m2 & ((1ull << (shift - 1)) - 1)) == 0; + uint64_t lastRemovedBit = (m2 >> (shift - 1)) & 1; + bool roundUp = (lastRemovedBit != 0) && (!trailingZeros || (((m2 >> shift) & 1) != 0)); + + uint64_t ieee_m2 = (m2 >> shift) + roundUp; + ieee_m2 &= (1ull << DOUBLE_MANTISSA_BITS) - 1; + + if (ieee_m2 == 0 && roundUp) { + ieee_e2++; + } + + // Pack sign, exponent, and mantissa into IEEE 754 format + // Match original Ryu: group sign+exponent, then shift and add mantissa + uint64_t ieee = (((((uint64_t) signedM) << DOUBLE_EXPONENT_BITS) | (uint64_t)ieee_e2) << DOUBLE_MANTISSA_BITS) | ieee_m2; + return int64Bits2Double(ieee); +} + +#endif // RYU_H diff --git a/ext/monitor/depend b/ext/monitor/depend index 38e21b3f66..0c7d54afc8 100644 --- a/ext/monitor/depend +++ b/ext/monitor/depend @@ -127,6 +127,7 @@ monitor.o: $(hdrdir)/ruby/internal/intern/re.h monitor.o: $(hdrdir)/ruby/internal/intern/ruby.h monitor.o: $(hdrdir)/ruby/internal/intern/select.h monitor.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +monitor.o: $(hdrdir)/ruby/internal/intern/set.h monitor.o: $(hdrdir)/ruby/internal/intern/signal.h monitor.o: $(hdrdir)/ruby/internal/intern/sprintf.h monitor.o: $(hdrdir)/ruby/internal/intern/string.h @@ -146,6 +147,7 @@ monitor.o: $(hdrdir)/ruby/internal/special_consts.h monitor.o: $(hdrdir)/ruby/internal/static_assert.h monitor.o: $(hdrdir)/ruby/internal/stdalign.h monitor.o: $(hdrdir)/ruby/internal/stdbool.h +monitor.o: $(hdrdir)/ruby/internal/stdckdint.h monitor.o: $(hdrdir)/ruby/internal/symbol.h monitor.o: $(hdrdir)/ruby/internal/value.h monitor.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/monitor/lib/monitor.rb b/ext/monitor/lib/monitor.rb index 31d6d2b3c4..82d0a75c56 100644 --- a/ext/monitor/lib/monitor.rb +++ b/ext/monitor/lib/monitor.rb @@ -143,13 +143,13 @@ module MonitorMixin private - def initialize(monitor) + def initialize(monitor) # :nodoc: @monitor = monitor @cond = Thread::ConditionVariable.new end end - def self.extend_object(obj) + def self.extend_object(obj) # :nodoc: super(obj) obj.__send__(:mon_initialize) end @@ -238,6 +238,8 @@ module MonitorMixin @mon_data_owner_object_id = self.object_id end + # Ensures that the MonitorMixin is owned by the current thread, + # otherwise raises an exception. def mon_check_owner @mon_data.mon_check_owner end @@ -254,6 +256,10 @@ end # end # class Monitor + # + # Creates a new MonitorMixin::ConditionVariable associated with the + # Monitor object. + # def new_cond ::MonitorMixin::ConditionVariable.new(self) end diff --git a/ext/monitor/monitor.c b/ext/monitor/monitor.c index 86613f6ade..c43751c4e2 100644 --- a/ext/monitor/monitor.c +++ b/ext/monitor/monitor.c @@ -4,28 +4,35 @@ struct rb_monitor { long count; - const VALUE owner; - const VALUE mutex; + VALUE owner; + VALUE mutex; }; static void monitor_mark(void *ptr) { struct rb_monitor *mc = ptr; - rb_gc_mark(mc->owner); - rb_gc_mark(mc->mutex); + rb_gc_mark_movable(mc->owner); + rb_gc_mark_movable(mc->mutex); } -static size_t -monitor_memsize(const void *ptr) +static void +monitor_compact(void *ptr) { - return sizeof(struct rb_monitor); + struct rb_monitor *mc = ptr; + mc->owner = rb_gc_location(mc->owner); + mc->mutex = rb_gc_location(mc->mutex); } static const rb_data_type_t monitor_data_type = { - "monitor", - {monitor_mark, RUBY_TYPED_DEFAULT_FREE, monitor_memsize,}, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED + .wrap_struct_name = "monitor", + .function = { + .dmark = monitor_mark, + .dfree = RUBY_TYPED_DEFAULT_FREE, + .dsize = NULL, // Fully embeded + .dcompact = monitor_compact, + }, + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE, }; static VALUE @@ -50,68 +57,124 @@ monitor_ptr(VALUE monitor) return mc; } -static int -mc_owner_p(struct rb_monitor *mc) +static bool +mc_owner_p(struct rb_monitor *mc, VALUE current_fiber) { - return mc->owner == rb_fiber_current(); + return mc->owner == current_fiber; } +/* + * call-seq: + * try_enter -> true or false + * + * Attempts to enter exclusive section. Returns +false+ if lock fails. + */ static VALUE monitor_try_enter(VALUE monitor) { struct rb_monitor *mc = monitor_ptr(monitor); - if (!mc_owner_p(mc)) { + VALUE current_fiber = rb_fiber_current(); + if (!mc_owner_p(mc, current_fiber)) { if (!rb_mutex_trylock(mc->mutex)) { return Qfalse; } - RB_OBJ_WRITE(monitor, &mc->owner, rb_fiber_current()); + RB_OBJ_WRITE(monitor, &mc->owner, current_fiber); mc->count = 0; } mc->count += 1; return Qtrue; } + +struct monitor_args { + VALUE monitor; + struct rb_monitor *mc; + VALUE current_fiber; +}; + +static inline void +monitor_args_init(struct monitor_args *args, VALUE monitor) +{ + args->monitor = monitor; + args->mc = monitor_ptr(monitor); + args->current_fiber = rb_fiber_current(); +} + +static void +monitor_enter0(struct monitor_args *args) +{ + if (!mc_owner_p(args->mc, args->current_fiber)) { + rb_mutex_lock(args->mc->mutex); + RB_OBJ_WRITE(args->monitor, &args->mc->owner, args->current_fiber); + args->mc->count = 0; + } + args->mc->count++; +} + +/* + * call-seq: + * enter -> nil + * + * Enters exclusive section. + */ static VALUE monitor_enter(VALUE monitor) { - struct rb_monitor *mc = monitor_ptr(monitor); - if (!mc_owner_p(mc)) { - rb_mutex_lock(mc->mutex); - RB_OBJ_WRITE(monitor, &mc->owner, rb_fiber_current()); - mc->count = 0; - } - mc->count++; + struct monitor_args args; + monitor_args_init(&args, monitor); + monitor_enter0(&args); return Qnil; } -static VALUE -monitor_check_owner(VALUE monitor) +static inline void +monitor_check_owner0(struct monitor_args *args) { - struct rb_monitor *mc = monitor_ptr(monitor); - if (!mc_owner_p(mc)) { + if (!mc_owner_p(args->mc, args->current_fiber)) { rb_raise(rb_eThreadError, "current fiber not owner"); } - return Qnil; } +/* :nodoc: */ static VALUE -monitor_exit(VALUE monitor) +monitor_check_owner(VALUE monitor) { - monitor_check_owner(monitor); + struct monitor_args args; + monitor_args_init(&args, monitor); + monitor_check_owner0(&args); + return Qnil; +} - struct rb_monitor *mc = monitor_ptr(monitor); +static void +monitor_exit0(struct monitor_args *args) +{ + monitor_check_owner0(args); - if (mc->count <= 0) rb_bug("monitor_exit: count:%d", (int)mc->count); - mc->count--; + if (args->mc->count <= 0) rb_bug("monitor_exit: count:%d", (int)args->mc->count); + args->mc->count--; - if (mc->count == 0) { - RB_OBJ_WRITE(monitor, &mc->owner, Qnil); - rb_mutex_unlock(mc->mutex); + if (args->mc->count == 0) { + RB_OBJ_WRITE(args->monitor, &args->mc->owner, Qnil); + rb_mutex_unlock(args->mc->mutex); } +} + +/* + * call-seq: + * exit -> nil + * + * Leaves exclusive section. + */ +static VALUE +monitor_exit(VALUE monitor) +{ + struct monitor_args args; + monitor_args_init(&args, monitor); + monitor_exit0(&args); return Qnil; } +/* :nodoc: */ static VALUE monitor_locked_p(VALUE monitor) { @@ -119,11 +182,12 @@ monitor_locked_p(VALUE monitor) return rb_mutex_locked_p(mc->mutex); } +/* :nodoc: */ static VALUE monitor_owned_p(VALUE monitor) { struct rb_monitor *mc = monitor_ptr(monitor); - return (rb_mutex_locked_p(mc->mutex) && mc_owner_p(mc)) ? Qtrue : Qfalse; + return rb_mutex_locked_p(mc->mutex) && mc_owner_p(mc, rb_fiber_current()) ? Qtrue : Qfalse; } static VALUE @@ -166,6 +230,7 @@ monitor_enter_for_cond(VALUE v) return Qnil; } +/* :nodoc: */ static VALUE monitor_wait_for_cond(VALUE monitor, VALUE cond, VALUE timeout) { @@ -188,16 +253,27 @@ monitor_sync_body(VALUE monitor) } static VALUE -monitor_sync_ensure(VALUE monitor) +monitor_sync_ensure(VALUE v_args) { - return monitor_exit(monitor); + monitor_exit0((struct monitor_args *)v_args); + return Qnil; } +/* + * call-seq: + * synchronize { } -> result of the block + * + * Enters exclusive section and executes the block. Leaves the exclusive + * section automatically when the block exits. See example under + * +MonitorMixin+. + */ static VALUE monitor_synchronize(VALUE monitor) { - monitor_enter(monitor); - return rb_ensure(monitor_sync_body, monitor, monitor_sync_ensure, monitor); + struct monitor_args args; + monitor_args_init(&args, monitor); + monitor_enter0(&args); + return rb_ensure(monitor_sync_body, (VALUE)&args, monitor_sync_ensure, (VALUE)&args); } void diff --git a/ext/objspace/depend b/ext/objspace/depend index aa0c5c5d7d..d9dfc0c42b 100644 --- a/ext/objspace/depend +++ b/ext/objspace/depend @@ -140,6 +140,7 @@ object_tracing.o: $(hdrdir)/ruby/internal/intern/re.h object_tracing.o: $(hdrdir)/ruby/internal/intern/ruby.h object_tracing.o: $(hdrdir)/ruby/internal/intern/select.h object_tracing.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/set.h object_tracing.o: $(hdrdir)/ruby/internal/intern/signal.h object_tracing.o: $(hdrdir)/ruby/internal/intern/sprintf.h object_tracing.o: $(hdrdir)/ruby/internal/intern/string.h @@ -159,6 +160,7 @@ object_tracing.o: $(hdrdir)/ruby/internal/special_consts.h object_tracing.o: $(hdrdir)/ruby/internal/static_assert.h object_tracing.o: $(hdrdir)/ruby/internal/stdalign.h object_tracing.o: $(hdrdir)/ruby/internal/stdbool.h +object_tracing.o: $(hdrdir)/ruby/internal/stdckdint.h object_tracing.o: $(hdrdir)/ruby/internal/symbol.h object_tracing.o: $(hdrdir)/ruby/internal/value.h object_tracing.o: $(hdrdir)/ruby/internal/value_type.h @@ -176,14 +178,17 @@ object_tracing.o: $(top_srcdir)/ccan/check_type/check_type.h object_tracing.o: $(top_srcdir)/ccan/container_of/container_of.h object_tracing.o: $(top_srcdir)/ccan/list/list.h object_tracing.o: $(top_srcdir)/ccan/str/str.h +object_tracing.o: $(top_srcdir)/id_table.h object_tracing.o: $(top_srcdir)/internal.h object_tracing.o: $(top_srcdir)/internal/array.h object_tracing.o: $(top_srcdir)/internal/basic_operators.h +object_tracing.o: $(top_srcdir)/internal/box.h object_tracing.o: $(top_srcdir)/internal/compilers.h object_tracing.o: $(top_srcdir)/internal/gc.h object_tracing.o: $(top_srcdir)/internal/imemo.h object_tracing.o: $(top_srcdir)/internal/sanitizers.h object_tracing.o: $(top_srcdir)/internal/serial.h +object_tracing.o: $(top_srcdir)/internal/set_table.h object_tracing.o: $(top_srcdir)/internal/static_assert.h object_tracing.o: $(top_srcdir)/internal/vm.h object_tracing.o: $(top_srcdir)/internal/warnings.h @@ -339,6 +344,7 @@ objspace.o: $(hdrdir)/ruby/internal/intern/re.h objspace.o: $(hdrdir)/ruby/internal/intern/ruby.h objspace.o: $(hdrdir)/ruby/internal/intern/select.h objspace.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +objspace.o: $(hdrdir)/ruby/internal/intern/set.h objspace.o: $(hdrdir)/ruby/internal/intern/signal.h objspace.o: $(hdrdir)/ruby/internal/intern/sprintf.h objspace.o: $(hdrdir)/ruby/internal/intern/string.h @@ -358,6 +364,7 @@ objspace.o: $(hdrdir)/ruby/internal/special_consts.h objspace.o: $(hdrdir)/ruby/internal/static_assert.h objspace.o: $(hdrdir)/ruby/internal/stdalign.h objspace.o: $(hdrdir)/ruby/internal/stdbool.h +objspace.o: $(hdrdir)/ruby/internal/stdckdint.h objspace.o: $(hdrdir)/ruby/internal/symbol.h objspace.o: $(hdrdir)/ruby/internal/value.h objspace.o: $(hdrdir)/ruby/internal/value_type.h @@ -384,6 +391,7 @@ objspace.o: $(top_srcdir)/id_table.h objspace.o: $(top_srcdir)/internal.h objspace.o: $(top_srcdir)/internal/array.h objspace.o: $(top_srcdir)/internal/basic_operators.h +objspace.o: $(top_srcdir)/internal/box.h objspace.o: $(top_srcdir)/internal/class.h objspace.o: $(top_srcdir)/internal/compilers.h objspace.o: $(top_srcdir)/internal/gc.h @@ -391,7 +399,9 @@ objspace.o: $(top_srcdir)/internal/hash.h objspace.o: $(top_srcdir)/internal/imemo.h objspace.o: $(top_srcdir)/internal/sanitizers.h objspace.o: $(top_srcdir)/internal/serial.h +objspace.o: $(top_srcdir)/internal/set_table.h objspace.o: $(top_srcdir)/internal/static_assert.h +objspace.o: $(top_srcdir)/internal/struct.h objspace.o: $(top_srcdir)/internal/variable.h objspace.o: $(top_srcdir)/internal/vm.h objspace.o: $(top_srcdir)/internal/warnings.h @@ -550,6 +560,7 @@ objspace_dump.o: $(hdrdir)/ruby/internal/intern/re.h objspace_dump.o: $(hdrdir)/ruby/internal/intern/ruby.h objspace_dump.o: $(hdrdir)/ruby/internal/intern/select.h objspace_dump.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/set.h objspace_dump.o: $(hdrdir)/ruby/internal/intern/signal.h objspace_dump.o: $(hdrdir)/ruby/internal/intern/sprintf.h objspace_dump.o: $(hdrdir)/ruby/internal/intern/string.h @@ -569,6 +580,7 @@ objspace_dump.o: $(hdrdir)/ruby/internal/special_consts.h objspace_dump.o: $(hdrdir)/ruby/internal/static_assert.h objspace_dump.o: $(hdrdir)/ruby/internal/stdalign.h objspace_dump.o: $(hdrdir)/ruby/internal/stdbool.h +objspace_dump.o: $(hdrdir)/ruby/internal/stdckdint.h objspace_dump.o: $(hdrdir)/ruby/internal/symbol.h objspace_dump.o: $(hdrdir)/ruby/internal/value.h objspace_dump.o: $(hdrdir)/ruby/internal/value_type.h @@ -590,10 +602,12 @@ objspace_dump.o: $(top_srcdir)/ccan/list/list.h objspace_dump.o: $(top_srcdir)/ccan/str/str.h objspace_dump.o: $(top_srcdir)/constant.h objspace_dump.o: $(top_srcdir)/debug_counter.h +objspace_dump.o: $(top_srcdir)/encindex.h objspace_dump.o: $(top_srcdir)/id_table.h objspace_dump.o: $(top_srcdir)/internal.h objspace_dump.o: $(top_srcdir)/internal/array.h objspace_dump.o: $(top_srcdir)/internal/basic_operators.h +objspace_dump.o: $(top_srcdir)/internal/box.h objspace_dump.o: $(top_srcdir)/internal/class.h objspace_dump.o: $(top_srcdir)/internal/compilers.h objspace_dump.o: $(top_srcdir)/internal/gc.h @@ -602,8 +616,10 @@ objspace_dump.o: $(top_srcdir)/internal/imemo.h objspace_dump.o: $(top_srcdir)/internal/io.h objspace_dump.o: $(top_srcdir)/internal/sanitizers.h objspace_dump.o: $(top_srcdir)/internal/serial.h +objspace_dump.o: $(top_srcdir)/internal/set_table.h objspace_dump.o: $(top_srcdir)/internal/static_assert.h objspace_dump.o: $(top_srcdir)/internal/string.h +objspace_dump.o: $(top_srcdir)/internal/struct.h objspace_dump.o: $(top_srcdir)/internal/variable.h objspace_dump.o: $(top_srcdir)/internal/vm.h objspace_dump.o: $(top_srcdir)/internal/warnings.h diff --git a/ext/objspace/object_tracing.c b/ext/objspace/object_tracing.c index c1c93c51f5..c06f1f68dd 100644 --- a/ext/objspace/object_tracing.c +++ b/ext/objspace/object_tracing.c @@ -53,6 +53,14 @@ make_unique_str(st_table *tbl, const char *str, long len) } } +static int +delete_unique_str_dec(st_data_t *key, st_data_t *value, st_data_t arg, int existing) +{ + assert(existing); + *value = arg; + return ST_CONTINUE; +} + static void delete_unique_str(st_table *tbl, const char *str) { @@ -66,7 +74,7 @@ delete_unique_str(st_table *tbl, const char *str) ruby_xfree((char *)n); } else { - st_insert(tbl, (st_data_t)str, n-1); + st_update(tbl, (st_data_t)str, delete_unique_str_dec, (st_data_t)(n-1)); } } } @@ -190,22 +198,25 @@ allocation_info_tracer_memsize(const void *ptr) } static int -hash_foreach_should_replace_key(st_data_t key, st_data_t value, st_data_t argp, int error) +allocation_info_tracer_compact_update_object_table_i(st_data_t key, st_data_t value, st_data_t data) { - VALUE allocated_object; + st_table *table = (st_table *)data; - allocated_object = (VALUE)value; - if (allocated_object != rb_gc_location(allocated_object)) { - return ST_REPLACE; + if (!rb_gc_pointer_to_heap_p(key)) { + struct allocation_info *info = (struct allocation_info *)value; + xfree(info); + return ST_DELETE; } - return ST_CONTINUE; -} + if (key != rb_gc_location(key)) { + DURING_GC_COULD_MALLOC_REGION_START(); + { + st_insert(table, rb_gc_location(key), value); + } + DURING_GC_COULD_MALLOC_REGION_END(); -static int -hash_replace_key(st_data_t *key, st_data_t *value, st_data_t argp, int existing) -{ - *key = rb_gc_location((VALUE)*key); + return ST_DELETE; + } return ST_CONTINUE; } @@ -216,7 +227,10 @@ allocation_info_tracer_compact(void *ptr) struct traceobj_arg *trace_arg = (struct traceobj_arg *)ptr; if (trace_arg->object_table && - st_foreach_with_replace(trace_arg->object_table, hash_foreach_should_replace_key, hash_replace_key, 0)) { + st_foreach( + trace_arg->object_table, + allocation_info_tracer_compact_update_object_table_i, + (st_data_t)trace_arg->object_table)) { rb_raise(rb_eRuntimeError, "hash modified during iteration"); } } @@ -399,6 +413,13 @@ object_allocations_reporter(FILE *out, void *ptr) fprintf(out, "== object_allocations_reporter: END\n"); } +/* + * call-seq: trace_object_allocations_debug_start + * + * Starts tracing object allocations for GC debugging. + * If you encounter the BUG "... is T_NONE" (and so on) on your + * application, please try this method at the beginning of your app. + */ static VALUE trace_object_allocations_debug_start(VALUE self) { diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c index 0a7896d5c6..1143e4801d 100644 --- a/ext/objspace/objspace.c +++ b/ext/objspace/objspace.c @@ -38,10 +38,11 @@ * information as only a *HINT*. Especially, the size of +T_DATA+ may not be * correct. * - * This method is only expected to work with C Ruby. + * This method is only expected to work with CRuby. * - * From Ruby 2.2, memsize_of(obj) returns a memory size includes - * sizeof(RVALUE). + * From Ruby 3.2 with Variable Width Allocation, it returns the actual slot + * size used plus any additional memory allocated outside the slot (such + * as external strings, arrays, or hash tables). */ static VALUE @@ -81,15 +82,15 @@ heap_iter(void *vstart, void *vend, size_t stride, void *ptr) VALUE v; for (v = (VALUE)vstart; v != (VALUE)vend; v += stride) { - void *poisoned = asan_poisoned_object_p(v); - asan_unpoison_object(v, false); + void *poisoned = rb_asan_poisoned_object_p(v); + rb_asan_unpoison_object(v, false); if (RBASIC(v)->flags) { (*ctx->cb)(v, ctx->data); } if (poisoned) { - asan_poison_object(v); + rb_asan_poison_object(v); } } @@ -107,28 +108,24 @@ each_object_with_flags(each_obj_with_flags cb, void *ctx) /* * call-seq: - * ObjectSpace.memsize_of_all([klass]) -> Integer + * ObjectSpace.memsize_of_all(klass = nil) -> integer * - * Return consuming memory size of all living objects in bytes. + * Returns the total memory size of all living objects in bytes. * - * If +klass+ (should be Class object) is given, return the total memory size - * of instances of the given class. + * ObjectSpace.memsize_of_all # => 12502001 * - * Note that the returned size is incomplete. You need to deal with this - * information as only a *HINT*. Especially, the size of +T_DATA+ may not be - * correct. + * If +klass+ is given (which must be a Class or Module), returns the total + * memory size of objects whose class is, or is a subclass, of +klass+. * - * Note that this method does *NOT* return total malloc'ed memory size. + * class MyClass; end + * ObjectSpace.memsize_of_all(MyClass) # => 0 + * o = MyClass.new + * ObjectSpace.memsize_of_all(MyClass) # => 40 * - * This method can be defined by the following Ruby code: - * - * def memsize_of_all klass = false - * total = 0 - * ObjectSpace.each_object{|e| - * total += ObjectSpace.memsize_of(e) if klass == false || e.kind_of?(klass) - * } - * total - * end + * Note that the value returned may be an underestimate of the actual amount + * of memory used. Therefore, the value returned should only be used as a hint, + * rather than a source of truth. In particular, the size of +T_DATA+ objects may + * not be correct. * * This method is only expected to work with C Ruby. */ @@ -140,6 +137,7 @@ memsize_of_all_m(int argc, VALUE *argv, VALUE self) if (argc > 0) { rb_scan_args(argc, argv, "01", &data.klass); + if (!NIL_P(data.klass)) rb_obj_is_kind_of(Qnil, data.klass); } each_object_with_flags(total_i, &data); @@ -169,8 +167,7 @@ setup_hash(int argc, VALUE *argv) hash = rb_hash_new(); } else if (!RHASH_EMPTY_P(hash)) { - /* WB: no new reference */ - st_foreach(RHASH_TBL_RAW(hash), set_zero_i, hash); + rb_hash_foreach(hash, set_zero_i, (st_data_t)hash); } return hash; @@ -295,28 +292,27 @@ size_t rb_sym_immortal_count(void); /* * call-seq: - * ObjectSpace.count_symbols([result_hash]) -> hash - * - * Counts symbols for each Symbol type. + * ObjectSpace.count_symbols(result_hash = nil) -> hash * - * This method is only for MRI developers interested in performance and memory - * usage of Ruby programs. + * Returns a hash containing the number of objects for each Symbol type. * - * If the optional argument, result_hash, is given, it is overwritten and - * returned. This is intended to avoid probe effect. + * The types of Symbols are the following: * - * Note: - * The contents of the returned hash is implementation defined. - * It may be changed in future. + * - +mortal_dynamic_symbol+: Symbols that are garbage collectable. + * - +immortal_dynamic_symbol+: Symbols that are objects allocated from the + * garbage collector, but are not garbage collectable. + * - +immortal_static_symbol+: Symbols that are not allocated from the + * garbage collector, and are thus not garbage collectable. + * - +immortal_symbol+: the sum of +immortal_dynamic_symbol+ and +immortal_static_symbol+. * - * This method is only expected to work with C Ruby. + * If the optional argument +result_hash+ is given, it is overwritten and + * returned. This is intended to avoid the probe effect. * - * On this version of MRI, they have 3 types of Symbols (and 1 total counts). + * This method is intended for developers interested in performance and memory + * usage of Ruby programs. The contents of the returned hash is implementation + * specific and may change in the future. * - * * mortal_dynamic_symbol: GC target symbols (collected by GC) - * * immortal_dynamic_symbol: Immortal symbols promoted from dynamic symbols (do not collected by GC) - * * immortal_static_symbol: Immortal symbols (do not collected by GC) - * * immortal_symbol: total immortal symbols (immortal_dynamic_symbol+immortal_static_symbol) + * This method is only expected to work with C Ruby. */ static VALUE @@ -336,35 +332,6 @@ count_symbols(int argc, VALUE *argv, VALUE os) return hash; } -/* - * call-seq: - * ObjectSpace.count_nodes([result_hash]) -> hash - * - * Counts nodes for each node type. - * - * This method is only for MRI developers interested in performance and memory - * usage of Ruby programs. - * - * It returns a hash as: - * - * {:NODE_METHOD=>2027, :NODE_FBODY=>1927, :NODE_CFUNC=>1798, ...} - * - * If the optional argument, result_hash, is given, it is overwritten and - * returned. This is intended to avoid probe effect. - * - * Note: - * The contents of the returned hash is implementation defined. - * It may be changed in future. - * - * This method is only expected to work with C Ruby. - */ - -static VALUE -count_nodes(int argc, VALUE *argv, VALUE os) -{ - return setup_hash(argc, argv); -} - static void cto_i(VALUE v, void *data) { @@ -394,32 +361,22 @@ cto_i(VALUE v, void *data) /* * call-seq: - * ObjectSpace.count_tdata_objects([result_hash]) -> hash + * ObjectSpace.count_tdata_objects(result_hash = nil) -> hash * - * Counts objects for each +T_DATA+ type. + * Returns a hash containing the number of objects for each +T_DATA+ type. + * The keys are Class objects when the +T_DATA+ object has an associated class, + * or Symbol objects of the name defined in the +rb_data_type_struct+ for internal + * +T_DATA+ objects. * - * This method is only for MRI developers interested in performance and memory - * usage of Ruby programs. + * ObjectSpace.count_tdata_objects + * # => {RBS::Location => 39255, marshal_compat_table: 1, Encoding => 103, mutex: 1, ... } * - * It returns a hash as: + * If the optional argument +result_hash+ is given, it is overwritten and + * returned. This is intended to avoid the probe effect. * - * {RubyVM::InstructionSequence=>504, :parser=>5, :barrier=>6, - * :mutex=>6, Proc=>60, RubyVM::Env=>57, Mutex=>1, Encoding=>99, - * ThreadGroup=>1, Binding=>1, Thread=>1, RubyVM=>1, :iseq=>1, - * Random=>1, ARGF.class=>1, Data=>1, :autoload=>3, Time=>2} - * # T_DATA objects existing at startup on r32276. - * - * If the optional argument, result_hash, is given, it is overwritten and - * returned. This is intended to avoid probe effect. - * - * The contents of the returned hash is implementation specific and may change - * in the future. - * - * In this version, keys are Class object or Symbol object. - * - * If object is kind of normal (accessible) object, the key is Class object. - * If object is not a kind of normal (internal) object, the key is symbol - * name, registered by rb_data_type_struct. + * This method is intended for developers interested in performance and memory + * usage of Ruby programs. The contents of the returned hash is implementation + * specific and may change in the future. * * This method is only expected to work with C Ruby. */ @@ -458,28 +415,22 @@ count_imemo_objects_i(VALUE v, void *data) /* * call-seq: - * ObjectSpace.count_imemo_objects([result_hash]) -> hash + * ObjectSpace.count_imemo_objects(result_hash = nil) -> hash * - * Counts objects for each +T_IMEMO+ type. + * Returns a hash containing the number of objects for each +T_IMEMO+ type. + * The keys are Symbol objects of the +T_IMEMO+ type name. + * +T_IMEMO+ objects are Ruby internal objects that are not visible to Ruby + * programs. * - * This method is only for MRI developers interested in performance and memory - * usage of Ruby programs. + * ObjectSpace.count_imemo_objects + * # => {imemo_callcache: 5482, imemo_constcache: 1258, imemo_ment: 13906, ... } * - * It returns a hash as: + * If the optional argument +result_hash+ is given, it is overwritten and + * returned. This is intended to avoid the probe effect. * - * {:imemo_ifunc=>8, - * :imemo_svar=>7, - * :imemo_cref=>509, - * :imemo_memo=>1, - * :imemo_throw_data=>1} - * - * If the optional argument, result_hash, is given, it is overwritten and - * returned. This is intended to avoid probe effect. - * - * The contents of the returned hash is implementation specific and may change - * in the future. - * - * In this version, keys are symbol objects. + * This method is intended for developers interested in performance and memory + * usage of Ruby programs. The contents of the returned hash is implementation + * specific and may change in the future. * * This method is only expected to work with C Ruby. */ @@ -500,11 +451,10 @@ count_imemo_objects(int argc, VALUE *argv, VALUE self) INIT_IMEMO_TYPE_ID(imemo_ment); INIT_IMEMO_TYPE_ID(imemo_iseq); INIT_IMEMO_TYPE_ID(imemo_tmpbuf); - INIT_IMEMO_TYPE_ID(imemo_ast); - INIT_IMEMO_TYPE_ID(imemo_parser_strterm); INIT_IMEMO_TYPE_ID(imemo_callinfo); INIT_IMEMO_TYPE_ID(imemo_callcache); INIT_IMEMO_TYPE_ID(imemo_constcache); + INIT_IMEMO_TYPE_ID(imemo_fields); #undef INIT_IMEMO_TYPE_ID } @@ -578,7 +528,7 @@ reachable_object_from_i(VALUE obj, void *data_ptr) VALUE key = obj; VALUE val = obj; - if (rb_objspace_markable_object_p(obj)) { + if (!rb_objspace_garbage_object_p(obj)) { if (NIL_P(rb_hash_lookup(data->refs, key))) { rb_hash_aset(data->refs, key, Qtrue); @@ -624,7 +574,7 @@ collect_values(st_data_t key, st_data_t value, st_data_t data) * * With this method, you can find memory leaks. * - * This method is only expected to work except with C Ruby. + * This method is only expected to work with C Ruby. * * Example: * ObjectSpace.reachable_objects_from(['a', 'b', 'c']) @@ -644,7 +594,7 @@ collect_values(st_data_t key, st_data_t value, st_data_t data) static VALUE reachable_objects_from(VALUE self, VALUE obj) { - if (rb_objspace_markable_object_p(obj)) { + if (!RB_SPECIAL_CONST_P(obj)) { struct rof_data data; if (rb_typeddata_is_kind_of(obj, &iow_data_type)) { @@ -691,7 +641,7 @@ reachable_object_from_root_i(const char *category, VALUE obj, void *ptr) rb_hash_aset(data->categories, category_str, category_objects); } - if (rb_objspace_markable_object_p(obj) && + if (!rb_objspace_garbage_object_p(obj) && obj != data->categories && obj != data->last_category_objects) { if (rb_objspace_internal_object_p(obj)) { @@ -793,7 +743,7 @@ objspace_internal_super_of(VALUE self, VALUE obj) case T_MODULE: case T_CLASS: case T_ICLASS: - super = RCLASS_SUPER(obj); + super = rb_class_super_of(obj); break; default: rb_raise(rb_eArgError, "class or module is expected"); @@ -835,7 +785,6 @@ Init_objspace(void) rb_define_module_function(rb_mObjSpace, "count_objects_size", count_objects_size, -1); rb_define_module_function(rb_mObjSpace, "count_symbols", count_symbols, -1); - rb_define_module_function(rb_mObjSpace, "count_nodes", count_nodes, -1); rb_define_module_function(rb_mObjSpace, "count_tdata_objects", count_tdata_objects, -1); rb_define_module_function(rb_mObjSpace, "count_imemo_objects", count_imemo_objects, -1); diff --git a/ext/objspace/objspace_dump.c b/ext/objspace/objspace_dump.c index 866a49eff4..da64698346 100644 --- a/ext/objspace/objspace_dump.c +++ b/ext/objspace/objspace_dump.c @@ -29,16 +29,17 @@ #include "ruby/util.h" #include "ruby/io.h" #include "vm_callinfo.h" -#include "vm_core.h" +#include "vm_sync.h" RUBY_EXTERN const char ruby_hexdigits[]; #define BUFFER_CAPACITY 4096 struct dump_config { - VALUE type; - VALUE stream; + VALUE given_output; + VALUE output_io; VALUE string; + FILE *stream; const char *root_category; VALUE cur_obj; VALUE cur_obj_klass; @@ -58,7 +59,7 @@ dump_flush(struct dump_config *dc) { if (dc->buffer_len) { if (dc->stream) { - size_t written = rb_io_bufwrite(dc->stream, dc->buffer, dc->buffer_len); + size_t written = fwrite(dc->buffer, sizeof(dc->buffer[0]), dc->buffer_len, dc->stream); if (written < dc->buffer_len) { MEMMOVE(dc->buffer, dc->buffer + written, char, dc->buffer_len - written); dc->buffer_len -= written; @@ -384,8 +385,6 @@ dump_object(VALUE obj, struct dump_config *dc) size_t memsize; struct allocation_info *ainfo = objspace_lookup_allocation_info(obj); rb_io_t *fptr; - ID flags[RB_OBJ_GC_FLAGS_MAX]; - size_t n, i; ID mid; if (SPECIAL_CONST_P(obj)) { @@ -395,9 +394,10 @@ dump_object(VALUE obj, struct dump_config *dc) dc->cur_obj = obj; dc->cur_obj_references = 0; - if (BUILTIN_TYPE(obj) == T_NODE || BUILTIN_TYPE(obj) == T_IMEMO) { + if (BUILTIN_TYPE(obj) == T_NODE || (BUILTIN_TYPE(obj) == T_IMEMO && !IMEMO_TYPE_P(obj, imemo_fields))) { dc->cur_obj_klass = 0; - } else { + } + else { dc->cur_obj_klass = RBASIC_CLASS(obj); } @@ -415,9 +415,11 @@ dump_object(VALUE obj, struct dump_config *dc) dump_append(dc, obj_type(obj)); dump_append(dc, "\""); - size_t shape_id = rb_shape_get_shape_id(obj); - dump_append(dc, ", \"shape_id\":"); - dump_append_sizet(dc, shape_id); + if (BUILTIN_TYPE(obj) != T_IMEMO || IMEMO_TYPE_P(obj, imemo_fields)) { + size_t shape_id = rb_obj_shape_id(obj) & SHAPE_ID_OFFSET_MASK; + dump_append(dc, ", \"shape_id\":"); + dump_append_sizet(dc, shape_id); + } dump_append(dc, ", \"slot_size\":"); dump_append_sizet(dc, dc->cur_page_slot_size); @@ -449,13 +451,16 @@ dump_object(VALUE obj, struct dump_config *dc) break; case imemo_callcache: - mid = vm_cc_cme((const struct rb_callcache *)obj)->called_id; - if (mid != 0) { - dump_append(dc, ", \"called_id\":"); - dump_append_id(dc, mid); - + { VALUE klass = ((const struct rb_callcache *)obj)->klass; - if (klass != 0) { + if (klass != Qundef) { + mid = vm_cc_cme((const struct rb_callcache *)obj)->called_id; + if (mid != 0) { + dump_append(dc, ", \"called_id\":"); + dump_append_id(dc, mid); + + } + dump_append(dc, ", \"receiver_class\":"); dump_append_ref(dc, klass); } @@ -476,6 +481,8 @@ dump_object(VALUE obj, struct dump_config *dc) dump_append(dc, ", \"embedded\":true"); if (FL_TEST(obj, RSTRING_FSTR)) dump_append(dc, ", \"fstring\":true"); + if (CHILLED_STRING_P(obj)) + dump_append(dc, ", \"chilled\":true"); if (STR_SHARED_P(obj)) dump_append(dc, ", \"shared\":true"); else @@ -536,7 +543,7 @@ dump_object(VALUE obj, struct dump_config *dc) case T_CLASS: dump_append(dc, ", \"variation_count\":"); - dump_append_d(dc, RCLASS_EXT(obj)->variation_count); + dump_append_d(dc, rb_class_variation_count(obj)); case T_MODULE: if (rb_class_get_superclass(obj)) { @@ -547,9 +554,8 @@ dump_object(VALUE obj, struct dump_config *dc) if (dc->cur_obj_klass) { VALUE mod_name = rb_mod_name(obj); if (!NIL_P(mod_name)) { - dump_append(dc, ", \"name\":\""); - dump_append(dc, RSTRING_PTR(mod_name)); - dump_append(dc, "\""); + dump_append(dc, ", \"name\":"); + dump_append_string_value(dc, mod_name); } else { VALUE real_mod_name = rb_mod_name(rb_class_real(obj)); @@ -560,7 +566,7 @@ dump_object(VALUE obj, struct dump_config *dc) } } - if (FL_TEST(obj, FL_SINGLETON)) { + if (rb_class_singleton_p(obj)) { dump_append(dc, ", \"singleton\":true"); } } @@ -581,13 +587,13 @@ dump_object(VALUE obj, struct dump_config *dc) break; case T_OBJECT: - if (FL_TEST(obj, ROBJECT_EMBED)) { + if (!FL_TEST_RAW(obj, ROBJECT_HEAP)) { dump_append(dc, ", \"embedded\":true"); } dump_append(dc, ", \"ivars\":"); - dump_append_lu(dc, ROBJECT_IV_COUNT(obj)); - if (rb_shape_obj_too_complex(obj)) { + dump_append_lu(dc, ROBJECT_FIELDS_COUNT(obj)); + if (rb_shape_obj_too_complex_p(obj)) { dump_append(dc, ", \"too_complex_shape\":true"); } break; @@ -636,14 +642,24 @@ dump_object(VALUE obj, struct dump_config *dc) dump_append_sizet(dc, memsize); } - if ((n = rb_obj_gc_flags(obj, flags, sizeof(flags))) > 0) { - dump_append(dc, ", \"flags\":{"); - for (i=0; i<n; i++) { - dump_append(dc, "\""); - dump_append(dc, rb_id2name(flags[i])); - dump_append(dc, "\":true"); - if (i != n-1) dump_append(dc, ", "); + struct rb_gc_object_metadata_entry *gc_metadata = rb_gc_object_metadata(obj); + for (int i = 0; gc_metadata[i].name != 0; i++) { + if (i == 0) { + dump_append(dc, ", \"flags\":{"); } + else { + dump_append(dc, ", "); + } + + dump_append(dc, "\""); + dump_append(dc, rb_id2name(gc_metadata[i].name)); + dump_append(dc, "\":"); + dump_append_special_const(dc, gc_metadata[i].val); + } + + /* If rb_gc_object_metadata had any entries, we need to close the opening + * `"flags":{`. */ + if (gc_metadata[0].name != 0) { dump_append(dc, "}"); } @@ -656,15 +672,15 @@ heap_i(void *vstart, void *vend, size_t stride, void *data) struct dump_config *dc = (struct dump_config *)data; VALUE v = (VALUE)vstart; for (; v != (VALUE)vend; v += stride) { - void *ptr = asan_poisoned_object_p(v); - asan_unpoison_object(v, false); + void *ptr = rb_asan_poisoned_object_p(v); + rb_asan_unpoison_object(v, false); dc->cur_page_slot_size = stride; if (dc->full_heap || RBASIC(v)->flags) dump_object(v, dc); if (ptr) { - asan_poison_object(v); + rb_asan_poison_object(v); } } return 0; @@ -695,16 +711,34 @@ root_obj_i(const char *category, VALUE obj, void *data) static void dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since, VALUE shapes) { - + dc->given_output = output; dc->full_heap = 0; dc->buffer_len = 0; if (TYPE(output) == T_STRING) { - dc->stream = Qfalse; + dc->stream = NULL; dc->string = output; } else { - dc->stream = output; + rb_io_t *fptr; + // Output should be an IO, typecheck and get a FILE* for writing. + // We cannot write with the usual IO code here because writes + // interleave with calls to rb_gc_mark(). The usual IO code can + // cause a thread switch, raise exceptions, and even run arbitrary + // ruby code through the fiber scheduler. + // + // Mark functions generally can't handle these possibilities so + // the usual IO code is unsafe in this context. (For example, + // there are many ways to crash when ruby code runs and mutates + // the execution context while rb_execution_context_mark() is in + // progress.) + // + // Using FILE* isn't perfect, but it avoids the most acute problems. + output = rb_io_get_io(output); + dc->output_io = rb_io_get_write_io(output); + rb_io_flush(dc->output_io); + GetOpenFile(dc->output_io, fptr); + dc->stream = rb_io_stdio_file(fptr); dc->string = Qfalse; } @@ -728,24 +762,25 @@ dump_result(struct dump_config *dc) { dump_flush(dc); + if (dc->stream) { + fflush(dc->stream); + } if (dc->string) { return dc->string; } - else { - rb_io_flush(dc->stream); - return dc->stream; - } + return dc->given_output; } -/* :nodoc: */ static VALUE -objspace_dump(VALUE os, VALUE obj, VALUE output) +dump_locked(void *args_p) { struct dump_config dc = {0,}; + VALUE obj = ((VALUE*)args_p)[0]; + VALUE output = ((VALUE*)args_p)[1]; + if (!RB_SPECIAL_CONST_P(obj)) { dc.cur_page_slot_size = rb_gc_obj_slot_size(obj); } - dump_output(&dc, output, Qnil, Qnil, Qnil); dump_object(obj, &dc); @@ -753,69 +788,73 @@ objspace_dump(VALUE os, VALUE obj, VALUE output) return dump_result(&dc); } +/* :nodoc: */ +static VALUE +objspace_dump(VALUE os, VALUE obj, VALUE output) +{ + VALUE args[2]; + args[0] = obj; + args[1] = output; + return rb_vm_lock_with_barrier(dump_locked, (void*)args); +} + static void -shape_i(rb_shape_t *shape, void *data) +shape_id_i(shape_id_t shape_id, void *data) { struct dump_config *dc = (struct dump_config *)data; - size_t shape_id = rb_shape_id(shape); if (shape_id < dc->shapes_since) { return; } dump_append(dc, "{\"address\":"); - dump_append_ref(dc, (VALUE)shape); + dump_append_ref(dc, (VALUE)RSHAPE(shape_id)); dump_append(dc, ", \"type\":\"SHAPE\", \"id\":"); dump_append_sizet(dc, shape_id); - if (shape->type != SHAPE_ROOT) { + if (RSHAPE_TYPE(shape_id) != SHAPE_ROOT) { dump_append(dc, ", \"parent_id\":"); - dump_append_lu(dc, shape->parent_id); + dump_append_lu(dc, RSHAPE_PARENT_RAW_ID(shape_id)); } dump_append(dc, ", \"depth\":"); - dump_append_sizet(dc, rb_shape_depth(shape)); + dump_append_sizet(dc, rb_shape_depth(shape_id)); - dump_append(dc, ", \"shape_type\":"); - switch((enum shape_type)shape->type) { + switch (RSHAPE_TYPE(shape_id)) { case SHAPE_ROOT: - dump_append(dc, "\"ROOT\""); + dump_append(dc, ", \"shape_type\":\"ROOT\""); break; case SHAPE_IVAR: - dump_append(dc, "\"IVAR\""); + dump_append(dc, ", \"shape_type\":\"IVAR\""); dump_append(dc, ",\"edge_name\":"); - dump_append_id(dc, shape->edge_name); + dump_append_id(dc, RSHAPE_EDGE_NAME(shape_id)); break; - case SHAPE_FROZEN: - dump_append(dc, "\"FROZEN\""); + case SHAPE_OBJ_ID: + dump_append(dc, ", \"shape_type\":\"OBJ_ID\""); break; - case SHAPE_T_OBJECT: - dump_append(dc, "\"T_OBJECT\""); - break; - case SHAPE_OBJ_TOO_COMPLEX: - dump_append(dc, "\"OBJ_TOO_COMPLEX\""); - break; - default: - rb_bug("[objspace] unexpected shape type"); } dump_append(dc, ", \"edges\":"); - dump_append_sizet(dc, rb_shape_edges_count(shape)); + dump_append_sizet(dc, rb_shape_edges_count(shape_id)); dump_append(dc, ", \"memsize\":"); - dump_append_sizet(dc, rb_shape_memsize(shape)); + dump_append_sizet(dc, rb_shape_memsize(shape_id)); dump_append(dc, "}\n"); } -/* :nodoc: */ static VALUE -objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since, VALUE shapes) +dump_all_locked(void *args_p) { struct dump_config dc = {0,}; + VALUE output = ((VALUE*)args_p)[0]; + VALUE full = ((VALUE*)args_p)[1]; + VALUE since = ((VALUE*)args_p)[2]; + VALUE shapes = ((VALUE*)args_p)[3]; + dump_output(&dc, output, full, since, shapes); if (!dc.partial_dump || dc.since == 0) { @@ -825,7 +864,7 @@ objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since, VALUE shapes) } if (RTEST(shapes)) { - rb_shape_each_shape(shape_i, &dc); + rb_shape_each_shape_id(shape_id_i, &dc); } /* dump all objects */ @@ -836,17 +875,41 @@ objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since, VALUE shapes) /* :nodoc: */ static VALUE -objspace_dump_shapes(VALUE os, VALUE output, VALUE shapes) +objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since, VALUE shapes) +{ + VALUE args[4]; + args[0] = output; + args[1] = full; + args[2] = since; + args[3] = shapes; + return rb_vm_lock_with_barrier(dump_all_locked, (void*)args); +} + +static VALUE +dump_shapes_locked(void *args_p) { struct dump_config dc = {0,}; + VALUE output = ((VALUE*)args_p)[0]; + VALUE shapes = ((VALUE*)args_p)[1]; + dump_output(&dc, output, Qfalse, Qnil, shapes); if (RTEST(shapes)) { - rb_shape_each_shape(shape_i, &dc); + rb_shape_each_shape_id(shape_id_i, &dc); } return dump_result(&dc); } +/* :nodoc: */ +static VALUE +objspace_dump_shapes(VALUE os, VALUE output, VALUE shapes) +{ + VALUE args[2]; + args[0] = output; + args[1] = shapes; + return rb_vm_lock_with_barrier(dump_shapes_locked, (void*)args); +} + void Init_objspace_dump(VALUE rb_mObjSpace) { @@ -854,11 +917,11 @@ Init_objspace_dump(VALUE rb_mObjSpace) #if 0 rb_mObjSpace = rb_define_module("ObjectSpace"); /* let rdoc know */ #endif +#ifdef HAVE_RB_EXT_RACTOR_SAFE + RB_EXT_RACTOR_SAFE(true); +#endif rb_define_module_function(rb_mObjSpace, "_dump", objspace_dump, 2); rb_define_module_function(rb_mObjSpace, "_dump_all", objspace_dump_all, 4); rb_define_module_function(rb_mObjSpace, "_dump_shapes", objspace_dump_shapes, 2); - - /* force create static IDs */ - rb_obj_gc_flags(rb_mObjSpace, 0, 0); } diff --git a/ext/openssl/History.md b/ext/openssl/History.md index 3249f6617a..419237ff16 100644 --- a/ext/openssl/History.md +++ b/ext/openssl/History.md @@ -1,3 +1,194 @@ +Version 4.0.0 +============= + +Compatibility +------------- + +* Ruby >= 2.7 +* OpenSSL >= 1.1.1, LibreSSL >= 3.9, and AWS-LC 1.66.0 + - Removed support for OpenSSL 1.0.2-1.1.0 and LibreSSL 3.1-3.8. + [[GitHub #835]](https://github.com/ruby/openssl/issues/835) + - Added support for AWS-LC. + [[GitHub #833]](https://github.com/ruby/openssl/issues/833) + + +Notable changes +--------------- + +* `OpenSSL::SSL` + - Reduce overhead when writing to `OpenSSL::SSL::SSLSocket`. `#syswrite` no + longer creates a temporary String object. + [[GitHub #831]](https://github.com/ruby/openssl/pull/831) + - Make `OpenSSL::SSL::SSLContext#min_version=` and `#max_version=` wrap the + corresponding OpenSSL APIs directly, and remove the fallback to SSL options. + [[GitHub #849]](https://github.com/ruby/openssl/pull/849) + - Add `OpenSSL::SSL::SSLContext#sigalgs=` and `#client_sigalgs=` for + specifying signature algorithms to use for connections. + [[GitHub #895]](https://github.com/ruby/openssl/pull/895) + - Rename `OpenSSL::SSL::SSLContext#ecdh_curves=` to `#groups=` following + the underlying OpenSSL API rename. This method is no longer specific to + ECDHE. The old method remains as an alias. + [[GitHub #900]](https://github.com/ruby/openssl/pull/900) + - Add `OpenSSL::SSL::SSLSocket#sigalg`, `#peer_sigalg`, and `#group` for + getting the signature algorithm and the key agreement group used in the + current connection. + [[GitHub #908]](https://github.com/ruby/openssl/pull/908) + - Enable `SSL_CTX_set_dh_auto()` for servers by default. + [[GitHub #924]](https://github.com/ruby/openssl/pull/924) + - Improve Ractor compatibility. Note that the internal-use constant + `OpenSSL::SSL::SSLContext::DEFAULT_PARAMS` is now frozen. + [[GitHub #925]](https://github.com/ruby/openssl/pull/925) +* `OpenSSL::PKey` + - Remove `OpenSSL::PKey::EC::Point#mul` support with array arguments. The + underlying OpenSSL API has been removed, and the method has been deprecated + since ruby/openssl v3.0.0. + [[GitHub #843]](https://github.com/ruby/openssl/pull/843) + - `OpenSSL::PKey::{RSA,DSA,DH}#params` uses `nil` to indicate missing fields + instead of the number `0`. + [[GitHub #774]](https://github.com/ruby/openssl/pull/774) + - Unify `OpenSSL::PKey::PKeyError` classes. The former subclasses + `OpenSSL::PKey::DHError`, `OpenSSL::PKey::DSAError`, + `OpenSSL::PKey::ECError`, and `OpenSSL::PKey::RSAError` have been merged + into a single class. + [[GitHub #929]](https://github.com/ruby/openssl/pull/929) +* `OpenSSL::Cipher` + - `OpenSSL::Cipher#encrypt` and `#decrypt` no longer accept arguments. + Passing passwords has been deprecated since Ruby 1.8.2 (released in 2004). + [[GitHub #887]](https://github.com/ruby/openssl/pull/887) + - `OpenSSL::Cipher#final` raises `OpenSSL::Cipher::AuthTagError` when the + integrity check fails for AEAD ciphers. `OpenSSL::Cipher::AuthTagError` is a + new subclass of `OpenSSL::Cipher::CipherError`, which was previously raised. + [[GitHub #939]](https://github.com/ruby/openssl/pull/939) + - `OpenSSL::Cipher.new` now raises `OpenSSL::Cipher::CipherError` instead of + `RuntimeError` when OpenSSL does not recognize the algorithm. + [[GitHub #958]](https://github.com/ruby/openssl/pull/958) + - Add support for "fetched" cipher algorithms with OpenSSL 3.0 or later. + [[GitHub #958]](https://github.com/ruby/openssl/pull/958) +* `OpenSSL::Digest` + - `OpenSSL::Digest.new` now raises `OpenSSL::Digest::DigestError` instead of + `RuntimeError` when OpenSSL does not recognize the algorithm. + [[GitHub #958]](https://github.com/ruby/openssl/pull/958) + - Add support for "fetched" digest algorithms with OpenSSL 3.0 or later. + [[GitHub #958]](https://github.com/ruby/openssl/pull/958) +* `OpenSSL::ASN1.decode` now assumes a 1950-2049 year range for `UTCTime` + according to RFC 5280. It previously used a 1969-2068 range. The encoder + has always used the 1950-2049 range. + [[GitHub #909]](https://github.com/ruby/openssl/pull/909) +* `OpenSSL::OpenSSLError`, the base class for all ruby/openssl errors, carry + an additional attribute `#errors` to keep the content of OpenSSL's error + queue. Also, add `#detailed_message` for Ruby 3.2 or later. + [[GitHub #976]](https://github.com/ruby/openssl/pull/976) +* `OpenSSL::PKCS7.new` raises `OpenSSL::PKCS7::PKCS7Error` instead of + `ArgumentError` on error to be consistent with other constructors. + [[GitHub #983]](https://github.com/ruby/openssl/pull/983) + + +Version 3.3.2 +============= + +Merged changes in 3.1.3 and 3.2.3. + + +Version 3.3.1 +============= + +Merged changes in 3.1.2 and 3.2.2. + + +Version 3.3.0 +============= + +Compatibility +------------- + +* Ruby version: 2.7 or later +* OpenSSL version: OpenSSL 1.0.2 or later, and LibreSSL 3.1 or later + +Notable changes +--------------- + +* `OpenSSL::SSL` + - `OpenSSL::SSL::SSLSocket#set_params` no longer sets `#min_version=` to TLS + 1.0 except when OpenSSL 1.0.2 is used. This has been done to disable + SSL 3.0, which is not supported by default in OpenSSL 1.1.0 or later, or in + LibreSSL. This lets it respect the system default if the system-wide + configuration file specifies a higher minimum protocol version. + [[GitHub #710]](https://github.com/ruby/openssl/pull/710) + - `OpenSSL::SSL::SSLSocket.new` no longer enables the `OpenSSL::SSL::OP_ALL` + SSL options by default and follows the system default. + [[GitHub #767]](https://github.com/ruby/openssl/pull/767) + - Add the following IO methods to `OpenSSL::SSL::SSLSocket`, which will pass + along to the underlying socket: `#local_address`, `#remote_address`, + `#close_on_exec=`, `#close_on_exec?`, `#wait`, `#wait_readable`, and + `#wait_writable`. + [[GitHub #708]](https://github.com/ruby/openssl/pull/708) + - Update `OpenSSL::SSL::SSLSocket#gets` to take the `chomp` keyword argument. + [[GitHub #708]](https://github.com/ruby/openssl/pull/708) + - Make `OpenSSL::SSL::SSLSocket` respect the `IO#timeout` value of the + underlying socket on Ruby 3.2 or later. `#timeout` and `#timeout=` methods + are also added. + [[GitHub #714]](https://github.com/ruby/openssl/pull/714) + - Add `OpenSSL::SSL::SSLSocket#close_read` and `#close_write`. + [[GitHub #743]](https://github.com/ruby/openssl/pull/743) + - Add `OpenSSL::Digest.digests` to get a list of all available digest + algorithms. + [[GitHub #726]](https://github.com/ruby/openssl/pull/726) + - Fix `OpenSSL::SSL::SSLSocket#read_nonblock` clearing the passed String + buffer when nothing can be read from the connection. + [[GitHub #739]](https://github.com/ruby/openssl/pull/739) +* Add `#to_text` methods to `OpenSSL::Timestamp::Response`, + `OpenSSL::Timestamp::Request`, `OpenSSL::Timestamp::TokenInfo`, and + `OpenSSL::PKCS7` to get a human-readable representation of the object. + [[GitHub #756]](https://github.com/ruby/openssl/pull/756) +* Add `OpenSSL::X509::Certificate#tbs_bytes` to get the DER encoding of the + TBSCertificate. + [[GitHub #753]](https://github.com/ruby/openssl/pull/753) +* Allow passing `nil` as the digest algorithm to `#sign` methods on + `OpenSSL::X509::Certificate`, `OpenSSL::X509::Request`, and + `OpenSSL::X509::CRL`. This adds supports for signing with EdDSA keys. + [[GitHub #761]](https://github.com/ruby/openssl/pull/761) + [[GitHub #804]](https://github.com/ruby/openssl/pull/804) +* Add `OpenSSL::SSL::SSLSocket#readbyte`. + [[GitHub #771]](https://github.com/ruby/openssl/pull/771) +* Change `OpenSSL::X509::Store#time=` to set the time to the `X509_VERIFY_PARAM` + in the `X509_STORE`. This allows `OpenSSL::Timestamp::Response#verify` to + verify a signature with the specified timestamp. + [[GitHub #770]](https://github.com/ruby/openssl/pull/770) +* Make `OpenSSL::PKCS7.encrypt`'s third parameter `cipher` mandatory. It had + an undocumented default value "RC2-40-CBC", which is not only insecure, but + also not supported in OpenSSL 3.0 or later. + [[GitHub #796]](https://github.com/ruby/openssl/pull/796) +* Make `OpenSSL::BN` shareable between ractors when frozen. + [[GitHub #808]](https://github.com/ruby/openssl/pull/808) +* Make `OpenSSL::Config` instances frozen by default, and make it shareable + between ractors. `OpenSSL::Config::DEFAULT_CONFIG_FILE` is also frozen. + [[GitHub #809]](https://github.com/ruby/openssl/pull/809) +* Add `OpenSSL::PKCS12#set_mac` to configure the MAC parameters and recalculate + a MAC for the content. + [[GitHub #788]](https://github.com/ruby/openssl/pull/788) + +And various non-user-visible changes and bug fixes. Please see the commit +history for more details. + + +Version 3.2.3 +============= + +Merged changes in 3.1.3. + + +Version 3.2.2 +============= + +Merged changes in 3.1.2. + + +Version 3.2.1 +============= + +Merged changes in 3.0.3. + + Version 3.2.0 ============= @@ -38,6 +229,39 @@ Notable changes [[GitHub #141]](https://github.com/ruby/openssl/pull/141) +Version 3.1.3 +============= + +Bug fixes +--------- + +* Fix missing NULL check for `EVP_PKEY_get0()` functions with OpenSSL 3.x. + [[GitHub #957]](https://github.com/ruby/openssl/pull/957) + + +Version 3.1.2 +============= + +Bug fixes +--------- + +* Fix crash when attempting to export an incomplete `OpenSSL::PKey::DSA` key. + [[GitHub #845]](https://github.com/ruby/openssl/issues/845) + [[GitHub #847]](https://github.com/ruby/openssl/pull/847) +* Remove the `OpenSSL::X509::V_FLAG_CRL_CHECK_ALL` flag from the default store + used by `OpenSSL::SSL::SSLContext#set_params`. It causes certificate + verification to fail with OpenSSL 3.6.0. It has no effect with any other + OpenSSL versions. + [[GitHub #949]](https://github.com/ruby/openssl/issues/949) + [[GitHub #950]](https://github.com/ruby/openssl/pull/950) + + +Version 3.1.1 +============= + +Merged changes in 3.0.3. + + Version 3.1.0 ============= @@ -74,6 +298,31 @@ Notable changes LibreSSL 3.6 and Ed25519 support in LibreSSL 3.7. +Version 3.0.3 +============= + +Bug fixes +--------- + +* Fix a performance regression introduced in v2.1.3 on a buffered write to + `SSLSocket`. + [[GitHub #706]](https://github.com/ruby/openssl/pull/706) +* Fix `OpenSSL::PKCS7` to handle PKCS#7 structures without content. + [[GitHub #690]](https://github.com/ruby/openssl/pull/690) + [[GitHub #752]](https://github.com/ruby/openssl/pull/752) +* Fix `OpenSSL::ASN1::ObjectId#==` with OIDs without a known name. + [[GitHub #791]](https://github.com/ruby/openssl/issues/791) + [[GitHub #792]](https://github.com/ruby/openssl/pull/792) +* Fix `OpenSSL::X509::Certificate#crl_uris` to handle CDP with multiple CRL + URIs. + [[GitHub #775]](https://github.com/ruby/openssl/issues/775) + [[GitHub #776]](https://github.com/ruby/openssl/pull/776) +* Fix `OpenSSL::Cipher#update` to always make the output buffer `String` + independent. + [[Bug #20937]](https://bugs.ruby-lang.org/issues/20937) + [[GitHub #824]](https://github.com/ruby/openssl/pull/824) + + Version 3.0.2 ============= diff --git a/ext/openssl/depend b/ext/openssl/depend index 0d03c85b80..435f4a1c68 100644 --- a/ext/openssl/depend +++ b/ext/openssl/depend @@ -142,6 +142,7 @@ ossl.o: $(hdrdir)/ruby/internal/intern/re.h ossl.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl.o: $(hdrdir)/ruby/internal/intern/select.h ossl.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl.o: $(hdrdir)/ruby/internal/intern/set.h ossl.o: $(hdrdir)/ruby/internal/intern/signal.h ossl.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl.o: $(hdrdir)/ruby/internal/intern/string.h @@ -161,6 +162,7 @@ ossl.o: $(hdrdir)/ruby/internal/special_consts.h ossl.o: $(hdrdir)/ruby/internal/static_assert.h ossl.o: $(hdrdir)/ruby/internal/stdalign.h ossl.o: $(hdrdir)/ruby/internal/stdbool.h +ossl.o: $(hdrdir)/ruby/internal/stdckdint.h ossl.o: $(hdrdir)/ruby/internal/symbol.h ossl.o: $(hdrdir)/ruby/internal/value.h ossl.o: $(hdrdir)/ruby/internal/value_type.h @@ -171,6 +173,7 @@ ossl.o: $(hdrdir)/ruby/io.h ossl.o: $(hdrdir)/ruby/missing.h ossl.o: $(hdrdir)/ruby/onigmo.h ossl.o: $(hdrdir)/ruby/oniguruma.h +ossl.o: $(hdrdir)/ruby/ractor.h ossl.o: $(hdrdir)/ruby/ruby.h ossl.o: $(hdrdir)/ruby/st.h ossl.o: $(hdrdir)/ruby/subst.h @@ -336,6 +339,7 @@ ossl_asn1.o: $(hdrdir)/ruby/internal/intern/re.h ossl_asn1.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_asn1.o: $(hdrdir)/ruby/internal/intern/select.h ossl_asn1.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_asn1.o: $(hdrdir)/ruby/internal/intern/set.h ossl_asn1.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_asn1.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_asn1.o: $(hdrdir)/ruby/internal/intern/string.h @@ -355,6 +359,7 @@ ossl_asn1.o: $(hdrdir)/ruby/internal/special_consts.h ossl_asn1.o: $(hdrdir)/ruby/internal/static_assert.h ossl_asn1.o: $(hdrdir)/ruby/internal/stdalign.h ossl_asn1.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_asn1.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_asn1.o: $(hdrdir)/ruby/internal/symbol.h ossl_asn1.o: $(hdrdir)/ruby/internal/value.h ossl_asn1.o: $(hdrdir)/ruby/internal/value_type.h @@ -365,6 +370,7 @@ ossl_asn1.o: $(hdrdir)/ruby/io.h ossl_asn1.o: $(hdrdir)/ruby/missing.h ossl_asn1.o: $(hdrdir)/ruby/onigmo.h ossl_asn1.o: $(hdrdir)/ruby/oniguruma.h +ossl_asn1.o: $(hdrdir)/ruby/ractor.h ossl_asn1.o: $(hdrdir)/ruby/ruby.h ossl_asn1.o: $(hdrdir)/ruby/st.h ossl_asn1.o: $(hdrdir)/ruby/subst.h @@ -530,6 +536,7 @@ ossl_bio.o: $(hdrdir)/ruby/internal/intern/re.h ossl_bio.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_bio.o: $(hdrdir)/ruby/internal/intern/select.h ossl_bio.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_bio.o: $(hdrdir)/ruby/internal/intern/set.h ossl_bio.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_bio.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_bio.o: $(hdrdir)/ruby/internal/intern/string.h @@ -549,6 +556,7 @@ ossl_bio.o: $(hdrdir)/ruby/internal/special_consts.h ossl_bio.o: $(hdrdir)/ruby/internal/static_assert.h ossl_bio.o: $(hdrdir)/ruby/internal/stdalign.h ossl_bio.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_bio.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_bio.o: $(hdrdir)/ruby/internal/symbol.h ossl_bio.o: $(hdrdir)/ruby/internal/value.h ossl_bio.o: $(hdrdir)/ruby/internal/value_type.h @@ -559,6 +567,7 @@ ossl_bio.o: $(hdrdir)/ruby/io.h ossl_bio.o: $(hdrdir)/ruby/missing.h ossl_bio.o: $(hdrdir)/ruby/onigmo.h ossl_bio.o: $(hdrdir)/ruby/oniguruma.h +ossl_bio.o: $(hdrdir)/ruby/ractor.h ossl_bio.o: $(hdrdir)/ruby/ruby.h ossl_bio.o: $(hdrdir)/ruby/st.h ossl_bio.o: $(hdrdir)/ruby/subst.h @@ -724,6 +733,7 @@ ossl_bn.o: $(hdrdir)/ruby/internal/intern/re.h ossl_bn.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_bn.o: $(hdrdir)/ruby/internal/intern/select.h ossl_bn.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_bn.o: $(hdrdir)/ruby/internal/intern/set.h ossl_bn.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_bn.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_bn.o: $(hdrdir)/ruby/internal/intern/string.h @@ -743,6 +753,7 @@ ossl_bn.o: $(hdrdir)/ruby/internal/special_consts.h ossl_bn.o: $(hdrdir)/ruby/internal/static_assert.h ossl_bn.o: $(hdrdir)/ruby/internal/stdalign.h ossl_bn.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_bn.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_bn.o: $(hdrdir)/ruby/internal/symbol.h ossl_bn.o: $(hdrdir)/ruby/internal/value.h ossl_bn.o: $(hdrdir)/ruby/internal/value_type.h @@ -919,6 +930,7 @@ ossl_cipher.o: $(hdrdir)/ruby/internal/intern/re.h ossl_cipher.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_cipher.o: $(hdrdir)/ruby/internal/intern/select.h ossl_cipher.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_cipher.o: $(hdrdir)/ruby/internal/intern/set.h ossl_cipher.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_cipher.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_cipher.o: $(hdrdir)/ruby/internal/intern/string.h @@ -938,6 +950,7 @@ ossl_cipher.o: $(hdrdir)/ruby/internal/special_consts.h ossl_cipher.o: $(hdrdir)/ruby/internal/static_assert.h ossl_cipher.o: $(hdrdir)/ruby/internal/stdalign.h ossl_cipher.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_cipher.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_cipher.o: $(hdrdir)/ruby/internal/symbol.h ossl_cipher.o: $(hdrdir)/ruby/internal/value.h ossl_cipher.o: $(hdrdir)/ruby/internal/value_type.h @@ -948,6 +961,7 @@ ossl_cipher.o: $(hdrdir)/ruby/io.h ossl_cipher.o: $(hdrdir)/ruby/missing.h ossl_cipher.o: $(hdrdir)/ruby/onigmo.h ossl_cipher.o: $(hdrdir)/ruby/oniguruma.h +ossl_cipher.o: $(hdrdir)/ruby/ractor.h ossl_cipher.o: $(hdrdir)/ruby/ruby.h ossl_cipher.o: $(hdrdir)/ruby/st.h ossl_cipher.o: $(hdrdir)/ruby/subst.h @@ -1113,6 +1127,7 @@ ossl_config.o: $(hdrdir)/ruby/internal/intern/re.h ossl_config.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_config.o: $(hdrdir)/ruby/internal/intern/select.h ossl_config.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_config.o: $(hdrdir)/ruby/internal/intern/set.h ossl_config.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_config.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_config.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1132,6 +1147,7 @@ ossl_config.o: $(hdrdir)/ruby/internal/special_consts.h ossl_config.o: $(hdrdir)/ruby/internal/static_assert.h ossl_config.o: $(hdrdir)/ruby/internal/stdalign.h ossl_config.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_config.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_config.o: $(hdrdir)/ruby/internal/symbol.h ossl_config.o: $(hdrdir)/ruby/internal/value.h ossl_config.o: $(hdrdir)/ruby/internal/value_type.h @@ -1142,6 +1158,7 @@ ossl_config.o: $(hdrdir)/ruby/io.h ossl_config.o: $(hdrdir)/ruby/missing.h ossl_config.o: $(hdrdir)/ruby/onigmo.h ossl_config.o: $(hdrdir)/ruby/oniguruma.h +ossl_config.o: $(hdrdir)/ruby/ractor.h ossl_config.o: $(hdrdir)/ruby/ruby.h ossl_config.o: $(hdrdir)/ruby/st.h ossl_config.o: $(hdrdir)/ruby/subst.h @@ -1307,6 +1324,7 @@ ossl_digest.o: $(hdrdir)/ruby/internal/intern/re.h ossl_digest.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_digest.o: $(hdrdir)/ruby/internal/intern/select.h ossl_digest.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_digest.o: $(hdrdir)/ruby/internal/intern/set.h ossl_digest.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_digest.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_digest.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1326,6 +1344,7 @@ ossl_digest.o: $(hdrdir)/ruby/internal/special_consts.h ossl_digest.o: $(hdrdir)/ruby/internal/static_assert.h ossl_digest.o: $(hdrdir)/ruby/internal/stdalign.h ossl_digest.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_digest.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_digest.o: $(hdrdir)/ruby/internal/symbol.h ossl_digest.o: $(hdrdir)/ruby/internal/value.h ossl_digest.o: $(hdrdir)/ruby/internal/value_type.h @@ -1336,6 +1355,7 @@ ossl_digest.o: $(hdrdir)/ruby/io.h ossl_digest.o: $(hdrdir)/ruby/missing.h ossl_digest.o: $(hdrdir)/ruby/onigmo.h ossl_digest.o: $(hdrdir)/ruby/oniguruma.h +ossl_digest.o: $(hdrdir)/ruby/ractor.h ossl_digest.o: $(hdrdir)/ruby/ruby.h ossl_digest.o: $(hdrdir)/ruby/st.h ossl_digest.o: $(hdrdir)/ruby/subst.h @@ -1501,6 +1521,7 @@ ossl_engine.o: $(hdrdir)/ruby/internal/intern/re.h ossl_engine.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_engine.o: $(hdrdir)/ruby/internal/intern/select.h ossl_engine.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_engine.o: $(hdrdir)/ruby/internal/intern/set.h ossl_engine.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_engine.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_engine.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1520,6 +1541,7 @@ ossl_engine.o: $(hdrdir)/ruby/internal/special_consts.h ossl_engine.o: $(hdrdir)/ruby/internal/static_assert.h ossl_engine.o: $(hdrdir)/ruby/internal/stdalign.h ossl_engine.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_engine.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_engine.o: $(hdrdir)/ruby/internal/symbol.h ossl_engine.o: $(hdrdir)/ruby/internal/value.h ossl_engine.o: $(hdrdir)/ruby/internal/value_type.h @@ -1530,6 +1552,7 @@ ossl_engine.o: $(hdrdir)/ruby/io.h ossl_engine.o: $(hdrdir)/ruby/missing.h ossl_engine.o: $(hdrdir)/ruby/onigmo.h ossl_engine.o: $(hdrdir)/ruby/oniguruma.h +ossl_engine.o: $(hdrdir)/ruby/ractor.h ossl_engine.o: $(hdrdir)/ruby/ruby.h ossl_engine.o: $(hdrdir)/ruby/st.h ossl_engine.o: $(hdrdir)/ruby/subst.h @@ -1695,6 +1718,7 @@ ossl_hmac.o: $(hdrdir)/ruby/internal/intern/re.h ossl_hmac.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_hmac.o: $(hdrdir)/ruby/internal/intern/select.h ossl_hmac.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_hmac.o: $(hdrdir)/ruby/internal/intern/set.h ossl_hmac.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_hmac.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_hmac.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1714,6 +1738,7 @@ ossl_hmac.o: $(hdrdir)/ruby/internal/special_consts.h ossl_hmac.o: $(hdrdir)/ruby/internal/static_assert.h ossl_hmac.o: $(hdrdir)/ruby/internal/stdalign.h ossl_hmac.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_hmac.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_hmac.o: $(hdrdir)/ruby/internal/symbol.h ossl_hmac.o: $(hdrdir)/ruby/internal/value.h ossl_hmac.o: $(hdrdir)/ruby/internal/value_type.h @@ -1724,6 +1749,7 @@ ossl_hmac.o: $(hdrdir)/ruby/io.h ossl_hmac.o: $(hdrdir)/ruby/missing.h ossl_hmac.o: $(hdrdir)/ruby/onigmo.h ossl_hmac.o: $(hdrdir)/ruby/oniguruma.h +ossl_hmac.o: $(hdrdir)/ruby/ractor.h ossl_hmac.o: $(hdrdir)/ruby/ruby.h ossl_hmac.o: $(hdrdir)/ruby/st.h ossl_hmac.o: $(hdrdir)/ruby/subst.h @@ -1889,6 +1915,7 @@ ossl_kdf.o: $(hdrdir)/ruby/internal/intern/re.h ossl_kdf.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_kdf.o: $(hdrdir)/ruby/internal/intern/select.h ossl_kdf.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_kdf.o: $(hdrdir)/ruby/internal/intern/set.h ossl_kdf.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_kdf.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_kdf.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1908,6 +1935,7 @@ ossl_kdf.o: $(hdrdir)/ruby/internal/special_consts.h ossl_kdf.o: $(hdrdir)/ruby/internal/static_assert.h ossl_kdf.o: $(hdrdir)/ruby/internal/stdalign.h ossl_kdf.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_kdf.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_kdf.o: $(hdrdir)/ruby/internal/symbol.h ossl_kdf.o: $(hdrdir)/ruby/internal/value.h ossl_kdf.o: $(hdrdir)/ruby/internal/value_type.h @@ -1918,6 +1946,7 @@ ossl_kdf.o: $(hdrdir)/ruby/io.h ossl_kdf.o: $(hdrdir)/ruby/missing.h ossl_kdf.o: $(hdrdir)/ruby/onigmo.h ossl_kdf.o: $(hdrdir)/ruby/oniguruma.h +ossl_kdf.o: $(hdrdir)/ruby/ractor.h ossl_kdf.o: $(hdrdir)/ruby/ruby.h ossl_kdf.o: $(hdrdir)/ruby/st.h ossl_kdf.o: $(hdrdir)/ruby/subst.h @@ -2083,6 +2112,7 @@ ossl_ns_spki.o: $(hdrdir)/ruby/internal/intern/re.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/intern/select.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_ns_spki.o: $(hdrdir)/ruby/internal/intern/set.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/intern/string.h @@ -2102,6 +2132,7 @@ ossl_ns_spki.o: $(hdrdir)/ruby/internal/special_consts.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/static_assert.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/stdalign.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_ns_spki.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/symbol.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/value.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/value_type.h @@ -2112,6 +2143,7 @@ ossl_ns_spki.o: $(hdrdir)/ruby/io.h ossl_ns_spki.o: $(hdrdir)/ruby/missing.h ossl_ns_spki.o: $(hdrdir)/ruby/onigmo.h ossl_ns_spki.o: $(hdrdir)/ruby/oniguruma.h +ossl_ns_spki.o: $(hdrdir)/ruby/ractor.h ossl_ns_spki.o: $(hdrdir)/ruby/ruby.h ossl_ns_spki.o: $(hdrdir)/ruby/st.h ossl_ns_spki.o: $(hdrdir)/ruby/subst.h @@ -2277,6 +2309,7 @@ ossl_ocsp.o: $(hdrdir)/ruby/internal/intern/re.h ossl_ocsp.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_ocsp.o: $(hdrdir)/ruby/internal/intern/select.h ossl_ocsp.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_ocsp.o: $(hdrdir)/ruby/internal/intern/set.h ossl_ocsp.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_ocsp.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_ocsp.o: $(hdrdir)/ruby/internal/intern/string.h @@ -2296,6 +2329,7 @@ ossl_ocsp.o: $(hdrdir)/ruby/internal/special_consts.h ossl_ocsp.o: $(hdrdir)/ruby/internal/static_assert.h ossl_ocsp.o: $(hdrdir)/ruby/internal/stdalign.h ossl_ocsp.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_ocsp.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_ocsp.o: $(hdrdir)/ruby/internal/symbol.h ossl_ocsp.o: $(hdrdir)/ruby/internal/value.h ossl_ocsp.o: $(hdrdir)/ruby/internal/value_type.h @@ -2306,6 +2340,7 @@ ossl_ocsp.o: $(hdrdir)/ruby/io.h ossl_ocsp.o: $(hdrdir)/ruby/missing.h ossl_ocsp.o: $(hdrdir)/ruby/onigmo.h ossl_ocsp.o: $(hdrdir)/ruby/oniguruma.h +ossl_ocsp.o: $(hdrdir)/ruby/ractor.h ossl_ocsp.o: $(hdrdir)/ruby/ruby.h ossl_ocsp.o: $(hdrdir)/ruby/st.h ossl_ocsp.o: $(hdrdir)/ruby/subst.h @@ -2471,6 +2506,7 @@ ossl_pkcs12.o: $(hdrdir)/ruby/internal/intern/re.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/intern/select.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_pkcs12.o: $(hdrdir)/ruby/internal/intern/set.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/intern/string.h @@ -2490,6 +2526,7 @@ ossl_pkcs12.o: $(hdrdir)/ruby/internal/special_consts.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/static_assert.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/stdalign.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_pkcs12.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/symbol.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/value.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/value_type.h @@ -2500,6 +2537,7 @@ ossl_pkcs12.o: $(hdrdir)/ruby/io.h ossl_pkcs12.o: $(hdrdir)/ruby/missing.h ossl_pkcs12.o: $(hdrdir)/ruby/onigmo.h ossl_pkcs12.o: $(hdrdir)/ruby/oniguruma.h +ossl_pkcs12.o: $(hdrdir)/ruby/ractor.h ossl_pkcs12.o: $(hdrdir)/ruby/ruby.h ossl_pkcs12.o: $(hdrdir)/ruby/st.h ossl_pkcs12.o: $(hdrdir)/ruby/subst.h @@ -2665,6 +2703,7 @@ ossl_pkcs7.o: $(hdrdir)/ruby/internal/intern/re.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/intern/select.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_pkcs7.o: $(hdrdir)/ruby/internal/intern/set.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/intern/string.h @@ -2684,6 +2723,7 @@ ossl_pkcs7.o: $(hdrdir)/ruby/internal/special_consts.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/static_assert.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/stdalign.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_pkcs7.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/symbol.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/value.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/value_type.h @@ -2694,6 +2734,7 @@ ossl_pkcs7.o: $(hdrdir)/ruby/io.h ossl_pkcs7.o: $(hdrdir)/ruby/missing.h ossl_pkcs7.o: $(hdrdir)/ruby/onigmo.h ossl_pkcs7.o: $(hdrdir)/ruby/oniguruma.h +ossl_pkcs7.o: $(hdrdir)/ruby/ractor.h ossl_pkcs7.o: $(hdrdir)/ruby/ruby.h ossl_pkcs7.o: $(hdrdir)/ruby/st.h ossl_pkcs7.o: $(hdrdir)/ruby/subst.h @@ -2859,6 +2900,7 @@ ossl_pkey.o: $(hdrdir)/ruby/internal/intern/re.h ossl_pkey.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_pkey.o: $(hdrdir)/ruby/internal/intern/select.h ossl_pkey.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_pkey.o: $(hdrdir)/ruby/internal/intern/set.h ossl_pkey.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_pkey.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_pkey.o: $(hdrdir)/ruby/internal/intern/string.h @@ -2878,6 +2920,7 @@ ossl_pkey.o: $(hdrdir)/ruby/internal/special_consts.h ossl_pkey.o: $(hdrdir)/ruby/internal/static_assert.h ossl_pkey.o: $(hdrdir)/ruby/internal/stdalign.h ossl_pkey.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_pkey.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_pkey.o: $(hdrdir)/ruby/internal/symbol.h ossl_pkey.o: $(hdrdir)/ruby/internal/value.h ossl_pkey.o: $(hdrdir)/ruby/internal/value_type.h @@ -2888,6 +2931,7 @@ ossl_pkey.o: $(hdrdir)/ruby/io.h ossl_pkey.o: $(hdrdir)/ruby/missing.h ossl_pkey.o: $(hdrdir)/ruby/onigmo.h ossl_pkey.o: $(hdrdir)/ruby/oniguruma.h +ossl_pkey.o: $(hdrdir)/ruby/ractor.h ossl_pkey.o: $(hdrdir)/ruby/ruby.h ossl_pkey.o: $(hdrdir)/ruby/st.h ossl_pkey.o: $(hdrdir)/ruby/subst.h @@ -3053,6 +3097,7 @@ ossl_pkey_dh.o: $(hdrdir)/ruby/internal/intern/re.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/intern/select.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_pkey_dh.o: $(hdrdir)/ruby/internal/intern/set.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/intern/string.h @@ -3072,6 +3117,7 @@ ossl_pkey_dh.o: $(hdrdir)/ruby/internal/special_consts.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/static_assert.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/stdalign.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_pkey_dh.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/symbol.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/value.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/value_type.h @@ -3082,6 +3128,7 @@ ossl_pkey_dh.o: $(hdrdir)/ruby/io.h ossl_pkey_dh.o: $(hdrdir)/ruby/missing.h ossl_pkey_dh.o: $(hdrdir)/ruby/onigmo.h ossl_pkey_dh.o: $(hdrdir)/ruby/oniguruma.h +ossl_pkey_dh.o: $(hdrdir)/ruby/ractor.h ossl_pkey_dh.o: $(hdrdir)/ruby/ruby.h ossl_pkey_dh.o: $(hdrdir)/ruby/st.h ossl_pkey_dh.o: $(hdrdir)/ruby/subst.h @@ -3247,6 +3294,7 @@ ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/intern/re.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/intern/select.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/intern/set.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/intern/string.h @@ -3266,6 +3314,7 @@ ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/special_consts.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/static_assert.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/stdalign.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/symbol.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/value.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/value_type.h @@ -3276,6 +3325,7 @@ ossl_pkey_dsa.o: $(hdrdir)/ruby/io.h ossl_pkey_dsa.o: $(hdrdir)/ruby/missing.h ossl_pkey_dsa.o: $(hdrdir)/ruby/onigmo.h ossl_pkey_dsa.o: $(hdrdir)/ruby/oniguruma.h +ossl_pkey_dsa.o: $(hdrdir)/ruby/ractor.h ossl_pkey_dsa.o: $(hdrdir)/ruby/ruby.h ossl_pkey_dsa.o: $(hdrdir)/ruby/st.h ossl_pkey_dsa.o: $(hdrdir)/ruby/subst.h @@ -3441,6 +3491,7 @@ ossl_pkey_ec.o: $(hdrdir)/ruby/internal/intern/re.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/intern/select.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_pkey_ec.o: $(hdrdir)/ruby/internal/intern/set.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/intern/string.h @@ -3460,6 +3511,7 @@ ossl_pkey_ec.o: $(hdrdir)/ruby/internal/special_consts.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/static_assert.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/stdalign.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_pkey_ec.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/symbol.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/value.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/value_type.h @@ -3470,6 +3522,7 @@ ossl_pkey_ec.o: $(hdrdir)/ruby/io.h ossl_pkey_ec.o: $(hdrdir)/ruby/missing.h ossl_pkey_ec.o: $(hdrdir)/ruby/onigmo.h ossl_pkey_ec.o: $(hdrdir)/ruby/oniguruma.h +ossl_pkey_ec.o: $(hdrdir)/ruby/ractor.h ossl_pkey_ec.o: $(hdrdir)/ruby/ruby.h ossl_pkey_ec.o: $(hdrdir)/ruby/st.h ossl_pkey_ec.o: $(hdrdir)/ruby/subst.h @@ -3635,6 +3688,7 @@ ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/intern/re.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/intern/select.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/intern/set.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/intern/string.h @@ -3654,6 +3708,7 @@ ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/special_consts.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/static_assert.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/stdalign.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/symbol.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/value.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/value_type.h @@ -3664,6 +3719,7 @@ ossl_pkey_rsa.o: $(hdrdir)/ruby/io.h ossl_pkey_rsa.o: $(hdrdir)/ruby/missing.h ossl_pkey_rsa.o: $(hdrdir)/ruby/onigmo.h ossl_pkey_rsa.o: $(hdrdir)/ruby/oniguruma.h +ossl_pkey_rsa.o: $(hdrdir)/ruby/ractor.h ossl_pkey_rsa.o: $(hdrdir)/ruby/ruby.h ossl_pkey_rsa.o: $(hdrdir)/ruby/st.h ossl_pkey_rsa.o: $(hdrdir)/ruby/subst.h @@ -3829,6 +3885,7 @@ ossl_provider.o: $(hdrdir)/ruby/internal/intern/re.h ossl_provider.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_provider.o: $(hdrdir)/ruby/internal/intern/select.h ossl_provider.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_provider.o: $(hdrdir)/ruby/internal/intern/set.h ossl_provider.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_provider.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_provider.o: $(hdrdir)/ruby/internal/intern/string.h @@ -3848,6 +3905,7 @@ ossl_provider.o: $(hdrdir)/ruby/internal/special_consts.h ossl_provider.o: $(hdrdir)/ruby/internal/static_assert.h ossl_provider.o: $(hdrdir)/ruby/internal/stdalign.h ossl_provider.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_provider.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_provider.o: $(hdrdir)/ruby/internal/symbol.h ossl_provider.o: $(hdrdir)/ruby/internal/value.h ossl_provider.o: $(hdrdir)/ruby/internal/value_type.h @@ -3858,6 +3916,7 @@ ossl_provider.o: $(hdrdir)/ruby/io.h ossl_provider.o: $(hdrdir)/ruby/missing.h ossl_provider.o: $(hdrdir)/ruby/onigmo.h ossl_provider.o: $(hdrdir)/ruby/oniguruma.h +ossl_provider.o: $(hdrdir)/ruby/ractor.h ossl_provider.o: $(hdrdir)/ruby/ruby.h ossl_provider.o: $(hdrdir)/ruby/st.h ossl_provider.o: $(hdrdir)/ruby/subst.h @@ -4023,6 +4082,7 @@ ossl_rand.o: $(hdrdir)/ruby/internal/intern/re.h ossl_rand.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_rand.o: $(hdrdir)/ruby/internal/intern/select.h ossl_rand.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_rand.o: $(hdrdir)/ruby/internal/intern/set.h ossl_rand.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_rand.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_rand.o: $(hdrdir)/ruby/internal/intern/string.h @@ -4042,6 +4102,7 @@ ossl_rand.o: $(hdrdir)/ruby/internal/special_consts.h ossl_rand.o: $(hdrdir)/ruby/internal/static_assert.h ossl_rand.o: $(hdrdir)/ruby/internal/stdalign.h ossl_rand.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_rand.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_rand.o: $(hdrdir)/ruby/internal/symbol.h ossl_rand.o: $(hdrdir)/ruby/internal/value.h ossl_rand.o: $(hdrdir)/ruby/internal/value_type.h @@ -4052,6 +4113,7 @@ ossl_rand.o: $(hdrdir)/ruby/io.h ossl_rand.o: $(hdrdir)/ruby/missing.h ossl_rand.o: $(hdrdir)/ruby/onigmo.h ossl_rand.o: $(hdrdir)/ruby/oniguruma.h +ossl_rand.o: $(hdrdir)/ruby/ractor.h ossl_rand.o: $(hdrdir)/ruby/ruby.h ossl_rand.o: $(hdrdir)/ruby/st.h ossl_rand.o: $(hdrdir)/ruby/subst.h @@ -4217,6 +4279,7 @@ ossl_ssl.o: $(hdrdir)/ruby/internal/intern/re.h ossl_ssl.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_ssl.o: $(hdrdir)/ruby/internal/intern/select.h ossl_ssl.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_ssl.o: $(hdrdir)/ruby/internal/intern/set.h ossl_ssl.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_ssl.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_ssl.o: $(hdrdir)/ruby/internal/intern/string.h @@ -4236,6 +4299,7 @@ ossl_ssl.o: $(hdrdir)/ruby/internal/special_consts.h ossl_ssl.o: $(hdrdir)/ruby/internal/static_assert.h ossl_ssl.o: $(hdrdir)/ruby/internal/stdalign.h ossl_ssl.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_ssl.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_ssl.o: $(hdrdir)/ruby/internal/symbol.h ossl_ssl.o: $(hdrdir)/ruby/internal/value.h ossl_ssl.o: $(hdrdir)/ruby/internal/value_type.h @@ -4246,6 +4310,7 @@ ossl_ssl.o: $(hdrdir)/ruby/io.h ossl_ssl.o: $(hdrdir)/ruby/missing.h ossl_ssl.o: $(hdrdir)/ruby/onigmo.h ossl_ssl.o: $(hdrdir)/ruby/oniguruma.h +ossl_ssl.o: $(hdrdir)/ruby/ractor.h ossl_ssl.o: $(hdrdir)/ruby/ruby.h ossl_ssl.o: $(hdrdir)/ruby/st.h ossl_ssl.o: $(hdrdir)/ruby/subst.h @@ -4411,6 +4476,7 @@ ossl_ssl_session.o: $(hdrdir)/ruby/internal/intern/re.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/intern/select.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_ssl_session.o: $(hdrdir)/ruby/internal/intern/set.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/intern/string.h @@ -4430,6 +4496,7 @@ ossl_ssl_session.o: $(hdrdir)/ruby/internal/special_consts.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/static_assert.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/stdalign.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_ssl_session.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/symbol.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/value.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/value_type.h @@ -4440,6 +4507,7 @@ ossl_ssl_session.o: $(hdrdir)/ruby/io.h ossl_ssl_session.o: $(hdrdir)/ruby/missing.h ossl_ssl_session.o: $(hdrdir)/ruby/onigmo.h ossl_ssl_session.o: $(hdrdir)/ruby/oniguruma.h +ossl_ssl_session.o: $(hdrdir)/ruby/ractor.h ossl_ssl_session.o: $(hdrdir)/ruby/ruby.h ossl_ssl_session.o: $(hdrdir)/ruby/st.h ossl_ssl_session.o: $(hdrdir)/ruby/subst.h @@ -4605,6 +4673,7 @@ ossl_ts.o: $(hdrdir)/ruby/internal/intern/re.h ossl_ts.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_ts.o: $(hdrdir)/ruby/internal/intern/select.h ossl_ts.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_ts.o: $(hdrdir)/ruby/internal/intern/set.h ossl_ts.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_ts.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_ts.o: $(hdrdir)/ruby/internal/intern/string.h @@ -4624,6 +4693,7 @@ ossl_ts.o: $(hdrdir)/ruby/internal/special_consts.h ossl_ts.o: $(hdrdir)/ruby/internal/static_assert.h ossl_ts.o: $(hdrdir)/ruby/internal/stdalign.h ossl_ts.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_ts.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_ts.o: $(hdrdir)/ruby/internal/symbol.h ossl_ts.o: $(hdrdir)/ruby/internal/value.h ossl_ts.o: $(hdrdir)/ruby/internal/value_type.h @@ -4634,6 +4704,7 @@ ossl_ts.o: $(hdrdir)/ruby/io.h ossl_ts.o: $(hdrdir)/ruby/missing.h ossl_ts.o: $(hdrdir)/ruby/onigmo.h ossl_ts.o: $(hdrdir)/ruby/oniguruma.h +ossl_ts.o: $(hdrdir)/ruby/ractor.h ossl_ts.o: $(hdrdir)/ruby/ruby.h ossl_ts.o: $(hdrdir)/ruby/st.h ossl_ts.o: $(hdrdir)/ruby/subst.h @@ -4799,6 +4870,7 @@ ossl_x509.o: $(hdrdir)/ruby/internal/intern/re.h ossl_x509.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_x509.o: $(hdrdir)/ruby/internal/intern/select.h ossl_x509.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_x509.o: $(hdrdir)/ruby/internal/intern/set.h ossl_x509.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_x509.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_x509.o: $(hdrdir)/ruby/internal/intern/string.h @@ -4818,6 +4890,7 @@ ossl_x509.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509.o: $(hdrdir)/ruby/internal/value.h ossl_x509.o: $(hdrdir)/ruby/internal/value_type.h @@ -4828,6 +4901,7 @@ ossl_x509.o: $(hdrdir)/ruby/io.h ossl_x509.o: $(hdrdir)/ruby/missing.h ossl_x509.o: $(hdrdir)/ruby/onigmo.h ossl_x509.o: $(hdrdir)/ruby/oniguruma.h +ossl_x509.o: $(hdrdir)/ruby/ractor.h ossl_x509.o: $(hdrdir)/ruby/ruby.h ossl_x509.o: $(hdrdir)/ruby/st.h ossl_x509.o: $(hdrdir)/ruby/subst.h @@ -4993,6 +5067,7 @@ ossl_x509attr.o: $(hdrdir)/ruby/internal/intern/re.h ossl_x509attr.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_x509attr.o: $(hdrdir)/ruby/internal/intern/select.h ossl_x509attr.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_x509attr.o: $(hdrdir)/ruby/internal/intern/set.h ossl_x509attr.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_x509attr.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_x509attr.o: $(hdrdir)/ruby/internal/intern/string.h @@ -5012,6 +5087,7 @@ ossl_x509attr.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509attr.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509attr.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509attr.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509attr.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509attr.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509attr.o: $(hdrdir)/ruby/internal/value.h ossl_x509attr.o: $(hdrdir)/ruby/internal/value_type.h @@ -5022,6 +5098,7 @@ ossl_x509attr.o: $(hdrdir)/ruby/io.h ossl_x509attr.o: $(hdrdir)/ruby/missing.h ossl_x509attr.o: $(hdrdir)/ruby/onigmo.h ossl_x509attr.o: $(hdrdir)/ruby/oniguruma.h +ossl_x509attr.o: $(hdrdir)/ruby/ractor.h ossl_x509attr.o: $(hdrdir)/ruby/ruby.h ossl_x509attr.o: $(hdrdir)/ruby/st.h ossl_x509attr.o: $(hdrdir)/ruby/subst.h @@ -5187,6 +5264,7 @@ ossl_x509cert.o: $(hdrdir)/ruby/internal/intern/re.h ossl_x509cert.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_x509cert.o: $(hdrdir)/ruby/internal/intern/select.h ossl_x509cert.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_x509cert.o: $(hdrdir)/ruby/internal/intern/set.h ossl_x509cert.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_x509cert.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_x509cert.o: $(hdrdir)/ruby/internal/intern/string.h @@ -5206,6 +5284,7 @@ ossl_x509cert.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509cert.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509cert.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509cert.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509cert.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509cert.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509cert.o: $(hdrdir)/ruby/internal/value.h ossl_x509cert.o: $(hdrdir)/ruby/internal/value_type.h @@ -5216,6 +5295,7 @@ ossl_x509cert.o: $(hdrdir)/ruby/io.h ossl_x509cert.o: $(hdrdir)/ruby/missing.h ossl_x509cert.o: $(hdrdir)/ruby/onigmo.h ossl_x509cert.o: $(hdrdir)/ruby/oniguruma.h +ossl_x509cert.o: $(hdrdir)/ruby/ractor.h ossl_x509cert.o: $(hdrdir)/ruby/ruby.h ossl_x509cert.o: $(hdrdir)/ruby/st.h ossl_x509cert.o: $(hdrdir)/ruby/subst.h @@ -5381,6 +5461,7 @@ ossl_x509crl.o: $(hdrdir)/ruby/internal/intern/re.h ossl_x509crl.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_x509crl.o: $(hdrdir)/ruby/internal/intern/select.h ossl_x509crl.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_x509crl.o: $(hdrdir)/ruby/internal/intern/set.h ossl_x509crl.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_x509crl.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_x509crl.o: $(hdrdir)/ruby/internal/intern/string.h @@ -5400,6 +5481,7 @@ ossl_x509crl.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509crl.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509crl.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509crl.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509crl.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509crl.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509crl.o: $(hdrdir)/ruby/internal/value.h ossl_x509crl.o: $(hdrdir)/ruby/internal/value_type.h @@ -5410,6 +5492,7 @@ ossl_x509crl.o: $(hdrdir)/ruby/io.h ossl_x509crl.o: $(hdrdir)/ruby/missing.h ossl_x509crl.o: $(hdrdir)/ruby/onigmo.h ossl_x509crl.o: $(hdrdir)/ruby/oniguruma.h +ossl_x509crl.o: $(hdrdir)/ruby/ractor.h ossl_x509crl.o: $(hdrdir)/ruby/ruby.h ossl_x509crl.o: $(hdrdir)/ruby/st.h ossl_x509crl.o: $(hdrdir)/ruby/subst.h @@ -5575,6 +5658,7 @@ ossl_x509ext.o: $(hdrdir)/ruby/internal/intern/re.h ossl_x509ext.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_x509ext.o: $(hdrdir)/ruby/internal/intern/select.h ossl_x509ext.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_x509ext.o: $(hdrdir)/ruby/internal/intern/set.h ossl_x509ext.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_x509ext.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_x509ext.o: $(hdrdir)/ruby/internal/intern/string.h @@ -5594,6 +5678,7 @@ ossl_x509ext.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509ext.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509ext.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509ext.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509ext.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509ext.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509ext.o: $(hdrdir)/ruby/internal/value.h ossl_x509ext.o: $(hdrdir)/ruby/internal/value_type.h @@ -5604,6 +5689,7 @@ ossl_x509ext.o: $(hdrdir)/ruby/io.h ossl_x509ext.o: $(hdrdir)/ruby/missing.h ossl_x509ext.o: $(hdrdir)/ruby/onigmo.h ossl_x509ext.o: $(hdrdir)/ruby/oniguruma.h +ossl_x509ext.o: $(hdrdir)/ruby/ractor.h ossl_x509ext.o: $(hdrdir)/ruby/ruby.h ossl_x509ext.o: $(hdrdir)/ruby/st.h ossl_x509ext.o: $(hdrdir)/ruby/subst.h @@ -5769,6 +5855,7 @@ ossl_x509name.o: $(hdrdir)/ruby/internal/intern/re.h ossl_x509name.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_x509name.o: $(hdrdir)/ruby/internal/intern/select.h ossl_x509name.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_x509name.o: $(hdrdir)/ruby/internal/intern/set.h ossl_x509name.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_x509name.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_x509name.o: $(hdrdir)/ruby/internal/intern/string.h @@ -5788,6 +5875,7 @@ ossl_x509name.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509name.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509name.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509name.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509name.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509name.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509name.o: $(hdrdir)/ruby/internal/value.h ossl_x509name.o: $(hdrdir)/ruby/internal/value_type.h @@ -5798,6 +5886,7 @@ ossl_x509name.o: $(hdrdir)/ruby/io.h ossl_x509name.o: $(hdrdir)/ruby/missing.h ossl_x509name.o: $(hdrdir)/ruby/onigmo.h ossl_x509name.o: $(hdrdir)/ruby/oniguruma.h +ossl_x509name.o: $(hdrdir)/ruby/ractor.h ossl_x509name.o: $(hdrdir)/ruby/ruby.h ossl_x509name.o: $(hdrdir)/ruby/st.h ossl_x509name.o: $(hdrdir)/ruby/subst.h @@ -5963,6 +6052,7 @@ ossl_x509req.o: $(hdrdir)/ruby/internal/intern/re.h ossl_x509req.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_x509req.o: $(hdrdir)/ruby/internal/intern/select.h ossl_x509req.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_x509req.o: $(hdrdir)/ruby/internal/intern/set.h ossl_x509req.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_x509req.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_x509req.o: $(hdrdir)/ruby/internal/intern/string.h @@ -5982,6 +6072,7 @@ ossl_x509req.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509req.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509req.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509req.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509req.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509req.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509req.o: $(hdrdir)/ruby/internal/value.h ossl_x509req.o: $(hdrdir)/ruby/internal/value_type.h @@ -5992,6 +6083,7 @@ ossl_x509req.o: $(hdrdir)/ruby/io.h ossl_x509req.o: $(hdrdir)/ruby/missing.h ossl_x509req.o: $(hdrdir)/ruby/onigmo.h ossl_x509req.o: $(hdrdir)/ruby/oniguruma.h +ossl_x509req.o: $(hdrdir)/ruby/ractor.h ossl_x509req.o: $(hdrdir)/ruby/ruby.h ossl_x509req.o: $(hdrdir)/ruby/st.h ossl_x509req.o: $(hdrdir)/ruby/subst.h @@ -6157,6 +6249,7 @@ ossl_x509revoked.o: $(hdrdir)/ruby/internal/intern/re.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/intern/select.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_x509revoked.o: $(hdrdir)/ruby/internal/intern/set.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/intern/string.h @@ -6176,6 +6269,7 @@ ossl_x509revoked.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509revoked.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/value.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/value_type.h @@ -6186,6 +6280,7 @@ ossl_x509revoked.o: $(hdrdir)/ruby/io.h ossl_x509revoked.o: $(hdrdir)/ruby/missing.h ossl_x509revoked.o: $(hdrdir)/ruby/onigmo.h ossl_x509revoked.o: $(hdrdir)/ruby/oniguruma.h +ossl_x509revoked.o: $(hdrdir)/ruby/ractor.h ossl_x509revoked.o: $(hdrdir)/ruby/ruby.h ossl_x509revoked.o: $(hdrdir)/ruby/st.h ossl_x509revoked.o: $(hdrdir)/ruby/subst.h @@ -6351,6 +6446,7 @@ ossl_x509store.o: $(hdrdir)/ruby/internal/intern/re.h ossl_x509store.o: $(hdrdir)/ruby/internal/intern/ruby.h ossl_x509store.o: $(hdrdir)/ruby/internal/intern/select.h ossl_x509store.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ossl_x509store.o: $(hdrdir)/ruby/internal/intern/set.h ossl_x509store.o: $(hdrdir)/ruby/internal/intern/signal.h ossl_x509store.o: $(hdrdir)/ruby/internal/intern/sprintf.h ossl_x509store.o: $(hdrdir)/ruby/internal/intern/string.h @@ -6370,6 +6466,7 @@ ossl_x509store.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509store.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509store.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509store.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509store.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509store.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509store.o: $(hdrdir)/ruby/internal/value.h ossl_x509store.o: $(hdrdir)/ruby/internal/value_type.h @@ -6380,6 +6477,7 @@ ossl_x509store.o: $(hdrdir)/ruby/io.h ossl_x509store.o: $(hdrdir)/ruby/missing.h ossl_x509store.o: $(hdrdir)/ruby/onigmo.h ossl_x509store.o: $(hdrdir)/ruby/oniguruma.h +ossl_x509store.o: $(hdrdir)/ruby/ractor.h ossl_x509store.o: $(hdrdir)/ruby/ruby.h ossl_x509store.o: $(hdrdir)/ruby/st.h ossl_x509store.o: $(hdrdir)/ruby/subst.h diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index 4119c72c48..a897c86b65 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -8,19 +8,12 @@ = Licence This program is licensed under the same licence as Ruby. - (See the file 'LICENCE'.) + (See the file 'COPYING'.) =end require "mkmf" -ssl_dirs = nil -if defined?(::TruffleRuby) - # Always respect the openssl prefix chosen by truffle/openssl-prefix - require 'truffle/openssl-prefix' - ssl_dirs = dir_config("openssl", ENV["OPENSSL_PREFIX"]) -else - ssl_dirs = dir_config("openssl") -end +ssl_dirs = dir_config("openssl") dir_config_given = ssl_dirs.any? _, ssl_ldir = ssl_dirs @@ -41,14 +34,16 @@ if ssl_ldir&.split(File::PATH_SEPARATOR)&.none? { |dir| File.directory?(dir) } "library directories." end -dir_config("kerberos") - Logging::message "=== OpenSSL for Ruby configurator ===\n" $defs.push("-D""OPENSSL_SUPPRESS_DEPRECATED") -have_func("rb_io_descriptor") -have_func("rb_io_maybe_wait(0, Qnil, Qnil, Qnil)", "ruby/io.h") # Ruby 3.1 +# Missing in TruffleRuby +have_func("rb_call_super_kw(0, NULL, 0)", "ruby.h") +# Ruby 3.1 +have_func("rb_io_descriptor", "ruby/io.h") +have_func("rb_io_maybe_wait(0, Qnil, Qnil, Qnil)", "ruby/io.h") +# Ruby 3.2 have_func("rb_io_timeout", "ruby/io.h") Logging::message "=== Checking for system dependent stuff... ===\n" @@ -67,7 +62,6 @@ end def find_openssl_library if $mswin || $mingw # required for static OpenSSL libraries - have_library("gdi32") # OpenSSL <= 1.0.2 (for RAND_screen()) have_library("crypt32") end @@ -84,12 +78,6 @@ def find_openssl_library return true end - # OpenSSL <= 1.0.2: libeay32.lib and ssleay32.lib. - if have_library("libeay32", "CRYPTO_malloc") && - have_library("ssleay32", "SSL_new") - return true - end - # LibreSSL: libcrypto-##.lib and libssl-##.lib, where ## is the ABI version # number. We have to find the version number out by scanning libpath. libpath = $LIBPATH.dup @@ -127,14 +115,15 @@ end version_ok = if have_macro("LIBRESSL_VERSION_NUMBER", "openssl/opensslv.h") is_libressl = true - checking_for("LibreSSL version >= 3.1.0") { - try_static_assert("LIBRESSL_VERSION_NUMBER >= 0x30100000L", "openssl/opensslv.h") } + checking_for("LibreSSL version >= 3.9.0") { + try_static_assert("LIBRESSL_VERSION_NUMBER >= 0x30900000L", "openssl/opensslv.h") } else - checking_for("OpenSSL version >= 1.0.2") { - try_static_assert("OPENSSL_VERSION_NUMBER >= 0x10002000L", "openssl/opensslv.h") } + is_openssl = true + checking_for("OpenSSL version >= 1.1.1") { + try_static_assert("OPENSSL_VERSION_NUMBER >= 0x10101000L", "openssl/opensslv.h") } end unless version_ok - raise "OpenSSL >= 1.0.2 or LibreSSL >= 3.1.0 is required" + raise "OpenSSL >= 1.1.1 or LibreSSL >= 3.9.0 is required" end # Prevent wincrypt.h from being included, which defines conflicting macro with openssl/x509.h @@ -144,66 +133,26 @@ end Logging::message "=== Checking for OpenSSL features... ===\n" evp_h = "openssl/evp.h".freeze -x509_h = "openssl/x509.h".freeze ts_h = "openssl/ts.h".freeze ssl_h = "openssl/ssl.h".freeze # compile options have_func("RAND_egd()", "openssl/rand.h") -engines = %w{dynamic 4758cca aep atalla chil - cswift nuron sureware ubsec padlock capi gmp gost cryptodev} -engines.each { |name| - have_func("ENGINE_load_#{name}()", "openssl/engine.h") -} - -# added in 1.1.0 -if !have_struct_member("SSL", "ctx", "openssl/ssl.h") || is_libressl - $defs.push("-DHAVE_OPAQUE_OPENSSL") -end -have_func("EVP_MD_CTX_new()", evp_h) -have_func("EVP_MD_CTX_free(NULL)", evp_h) -have_func("EVP_MD_CTX_pkey_ctx(NULL)", evp_h) -have_func("X509_STORE_get_ex_data(NULL, 0)", x509_h) -have_func("X509_STORE_set_ex_data(NULL, 0, NULL)", x509_h) -have_func("X509_STORE_get_ex_new_index(0, NULL, NULL, NULL, NULL)", x509_h) -have_func("X509_CRL_get0_signature(NULL, NULL, NULL)", x509_h) -have_func("X509_REQ_get0_signature(NULL, NULL, NULL)", x509_h) -have_func("X509_REVOKED_get0_serialNumber(NULL)", x509_h) -have_func("X509_REVOKED_get0_revocationDate(NULL)", x509_h) -have_func("X509_get0_tbs_sigalg(NULL)", x509_h) -have_func("X509_STORE_CTX_get0_untrusted(NULL)", x509_h) -have_func("X509_STORE_CTX_get0_cert(NULL)", x509_h) -have_func("X509_STORE_CTX_get0_chain(NULL)", x509_h) -have_func("OCSP_SINGLERESP_get0_id(NULL)", "openssl/ocsp.h") -have_func("SSL_CTX_get_ciphers(NULL)", ssl_h) -have_func("X509_up_ref(NULL)", x509_h) -have_func("X509_CRL_up_ref(NULL)", x509_h) -have_func("X509_STORE_up_ref(NULL)", x509_h) -have_func("SSL_SESSION_up_ref(NULL)", ssl_h) -have_func("EVP_PKEY_up_ref(NULL)", evp_h) -have_func("SSL_CTX_set_min_proto_version(NULL, 0)", ssl_h) -have_func("SSL_CTX_get_security_level(NULL)", ssl_h) -have_func("X509_get0_notBefore(NULL)", x509_h) -have_func("SSL_SESSION_get_protocol_version(NULL)", ssl_h) -have_func("TS_STATUS_INFO_get0_status(NULL)", ts_h) -have_func("TS_STATUS_INFO_get0_text(NULL)", ts_h) -have_func("TS_STATUS_INFO_get0_failure_info(NULL)", ts_h) -have_func("TS_VERIFY_CTS_set_certs(NULL, NULL)", ts_h) -have_func("TS_VERIFY_CTX_set_store(NULL, NULL)", ts_h) -have_func("TS_VERIFY_CTX_add_flags(NULL, 0)", ts_h) -have_func("TS_RESP_CTX_set_time_cb(NULL, NULL, NULL)", ts_h) + +# added in OpenSSL 1.0.2, not in LibreSSL yet +have_func("SSL_CTX_set1_sigalgs_list(NULL, NULL)", ssl_h) +# added in OpenSSL 1.0.2, not in LibreSSL or AWS-LC yet +have_func("SSL_CTX_set1_client_sigalgs_list(NULL, NULL)", ssl_h) + +# added in 1.1.0, currently not in LibreSSL have_func("EVP_PBE_scrypt(\"\", 0, (unsigned char *)\"\", 0, 0, 0, 0, 0, NULL, 0)", evp_h) -have_func("SSL_CTX_set_post_handshake_auth(NULL, 0)", ssl_h) -# added in 1.1.1 +# added in OpenSSL 1.1.1 and LibreSSL 3.5.0, then removed in LibreSSL 4.0.0 have_func("EVP_PKEY_check(NULL)", evp_h) -have_func("EVP_PKEY_new_raw_private_key(0, NULL, (unsigned char *)\"\", 0)", evp_h) -have_func("SSL_CTX_set_ciphersuites(NULL, \"\")", ssl_h) # added in 3.0.0 -have_func("SSL_set0_tmp_dh_pkey(NULL, NULL)", ssl_h) +have_func("SSL_CTX_set0_tmp_dh_pkey(NULL, NULL)", ssl_h) have_func("ERR_get_error_all(NULL, NULL, NULL, NULL, NULL)", "openssl/err.h") -have_func("TS_VERIFY_CTX_set_certs(NULL, NULL)", ts_h) have_func("SSL_CTX_load_verify_file(NULL, \"\")", ssl_h) have_func("BN_check_prime(NULL, NULL, NULL)", "openssl/bn.h") have_func("EVP_MD_CTX_get0_md(NULL)", evp_h) @@ -211,6 +160,15 @@ have_func("EVP_MD_CTX_get_pkey_ctx(NULL)", evp_h) have_func("EVP_PKEY_eq(NULL, NULL)", evp_h) have_func("EVP_PKEY_dup(NULL)", evp_h) +# added in 3.2.0 +have_func("SSL_get0_group_name(NULL)", ssl_h) + +# added in 3.4.0 +have_func("TS_VERIFY_CTX_set0_certs(NULL, NULL)", ts_h) + +# added in 3.5.0 +have_func("SSL_get0_peer_signature_name(NULL, NULL)", ssl_h) + Logging::message "=== Checking done. ===\n" # Append flags from environment variables. diff --git a/ext/openssl/lib/openssl.rb b/ext/openssl/lib/openssl.rb index 8a342f15b6..98fa8d39f2 100644 --- a/ext/openssl/lib/openssl.rb +++ b/ext/openssl/lib/openssl.rb @@ -7,28 +7,32 @@ = Licence This program is licensed under the same licence as Ruby. - (See the file 'LICENCE'.) + (See the file 'COPYING'.) =end require 'openssl.so' require_relative 'openssl/bn' -require_relative 'openssl/pkey' require_relative 'openssl/cipher' require_relative 'openssl/digest' require_relative 'openssl/hmac' -require_relative 'openssl/x509' -require_relative 'openssl/ssl' require_relative 'openssl/pkcs5' +require_relative 'openssl/pkey' +require_relative 'openssl/ssl' require_relative 'openssl/version' +require_relative 'openssl/x509' module OpenSSL - # call-seq: - # OpenSSL.secure_compare(string, string) -> boolean + # :call-seq: + # OpenSSL.secure_compare(string, string) -> true or false # # Constant time memory comparison. Inputs are hashed using SHA-256 to mask # the length of the secret. Returns +true+ if the strings are identical, # +false+ otherwise. + # + # This method is expensive due to the SHA-256 hashing. In most cases, where + # the input lengths are known to be equal or are not sensitive, + # OpenSSL.fixed_length_secure_compare should be used instead. def self.secure_compare(a, b) hashed_a = OpenSSL::Digest.digest('SHA256', a) hashed_b = OpenSSL::Digest.digest('SHA256', b) diff --git a/ext/openssl/lib/openssl/bn.rb b/ext/openssl/lib/openssl/bn.rb index 0a5e11b4c2..e4889a140c 100644 --- a/ext/openssl/lib/openssl/bn.rb +++ b/ext/openssl/lib/openssl/bn.rb @@ -10,7 +10,7 @@ # # = Licence # This program is licensed under the same licence as Ruby. -# (See the file 'LICENCE'.) +# (See the file 'COPYING'.) #++ module OpenSSL diff --git a/ext/openssl/lib/openssl/buffering.rb b/ext/openssl/lib/openssl/buffering.rb index 68aa7bc970..1464a4292d 100644 --- a/ext/openssl/lib/openssl/buffering.rb +++ b/ext/openssl/lib/openssl/buffering.rb @@ -8,7 +8,7 @@ # #= Licence # This program is licensed under the same licence as Ruby. -# (See the file 'LICENCE'.) +# (See the file 'COPYING'.) #++ ## @@ -24,25 +24,21 @@ module OpenSSL::Buffering # A buffer which will retain binary encoding. class Buffer < String - BINARY = Encoding::BINARY - - def initialize - super - - force_encoding(BINARY) - end + unless String.method_defined?(:append_as_bytes) + alias_method :_append, :<< + def append_as_bytes(string) + if string.encoding == Encoding::BINARY + _append(string) + else + _append(string.b) + end - def << string - if string.encoding == BINARY - super(string) - else - super(string.b) + self end - - return self end - alias concat << + undef_method :concat + undef_method :<< end ## @@ -77,7 +73,7 @@ module OpenSSL::Buffering def fill_rbuff begin - @rbuffer << self.sysread(BLOCK_SIZE) + @rbuffer.append_as_bytes(self.sysread(BLOCK_SIZE)) rescue Errno::EAGAIN retry rescue EOFError @@ -107,6 +103,12 @@ module OpenSSL::Buffering read(1)&.ord end + # Get the next 8bit byte. Raises EOFError on EOF + def readbyte + raise EOFError if eof? + getbyte + end + ## # Reads _size_ bytes from the stream. If _buf_ is provided it must # reference a string which will receive the data. @@ -346,17 +348,32 @@ module OpenSSL::Buffering def do_write(s) @wbuffer = Buffer.new unless defined? @wbuffer - @wbuffer << s - @wbuffer.force_encoding(Encoding::BINARY) + @wbuffer.append_as_bytes(s) + @sync ||= false - if @sync or @wbuffer.size > BLOCK_SIZE - until @wbuffer.empty? - begin - nwrote = syswrite(@wbuffer) - rescue Errno::EAGAIN - retry + buffer_size = @wbuffer.bytesize + if @sync or buffer_size > BLOCK_SIZE + nwrote = 0 + begin + while nwrote < buffer_size do + begin + chunk = if nwrote > 0 + @wbuffer.byteslice(nwrote, @wbuffer.bytesize) + else + @wbuffer + end + + nwrote += syswrite(chunk) + rescue Errno::EAGAIN + retry + end + end + ensure + if nwrote < @wbuffer.bytesize + @wbuffer[0, nwrote] = "" + else + @wbuffer.clear end - @wbuffer[0, nwrote] = "" end end end @@ -433,10 +450,10 @@ module OpenSSL::Buffering def puts(*args) s = Buffer.new if args.empty? - s << "\n" + s.append_as_bytes("\n") end args.each{|arg| - s << arg.to_s + s.append_as_bytes(arg.to_s) s.sub!(/(?<!\n)\z/, "\n") } do_write(s) @@ -450,7 +467,7 @@ module OpenSSL::Buffering def print(*args) s = Buffer.new - args.each{ |arg| s << arg.to_s } + args.each{ |arg| s.append_as_bytes(arg.to_s) } do_write(s) nil end diff --git a/ext/openssl/lib/openssl/cipher.rb b/ext/openssl/lib/openssl/cipher.rb index 8ad8c35dd3..ab75ac8e1a 100644 --- a/ext/openssl/lib/openssl/cipher.rb +++ b/ext/openssl/lib/openssl/cipher.rb @@ -9,7 +9,7 @@ # # = Licence # This program is licensed under the same licence as Ruby. -# (See the file 'LICENCE'.) +# (See the file 'COPYING'.) #++ module OpenSSL diff --git a/ext/openssl/lib/openssl/digest.rb b/ext/openssl/lib/openssl/digest.rb index 0f35ddadd3..46ddfd6021 100644 --- a/ext/openssl/lib/openssl/digest.rb +++ b/ext/openssl/lib/openssl/digest.rb @@ -9,7 +9,7 @@ # # = Licence # This program is licensed under the same licence as Ruby. -# (See the file 'LICENCE'.) +# (See the file 'COPYING'.) #++ module OpenSSL @@ -57,7 +57,7 @@ module OpenSSL # OpenSSL::Digest("MD5") # # => OpenSSL::Digest::MD5 # - # Digest("Foo") + # OpenSSL::Digest("Foo") # # => NameError: wrong constant name Foo def Digest(name) diff --git a/ext/openssl/lib/openssl/marshal.rb b/ext/openssl/lib/openssl/marshal.rb index af5647192a..eb8eda4748 100644 --- a/ext/openssl/lib/openssl/marshal.rb +++ b/ext/openssl/lib/openssl/marshal.rb @@ -9,7 +9,7 @@ # # = Licence # This program is licensed under the same licence as Ruby. -# (See the file 'LICENCE'.) +# (See the file 'COPYING'.) #++ module OpenSSL module Marshal diff --git a/ext/openssl/lib/openssl/pkey.rb b/ext/openssl/lib/openssl/pkey.rb index 0414658a10..39871e15dd 100644 --- a/ext/openssl/lib/openssl/pkey.rb +++ b/ext/openssl/lib/openssl/pkey.rb @@ -7,6 +7,9 @@ require_relative 'marshal' module OpenSSL::PKey + # Alias of PKeyError. Before version 4.0.0, this was a subclass of PKeyError. + DHError = PKeyError + class DH include OpenSSL::Marshal @@ -35,6 +38,18 @@ module OpenSSL::PKey end # :call-seq: + # dh.params -> hash + # + # Stores all parameters of key to a Hash. + # + # The hash has keys 'p', 'q', 'g', 'pub_key', and 'priv_key'. + def params + %w{p q g pub_key priv_key}.map { |name| + [name, send(name)] + }.to_h + end + + # :call-seq: # dh.compute_key(pub_bn) -> string # # Returns a String containing a shared secret computed from the other @@ -90,7 +105,7 @@ module OpenSSL::PKey # puts dh0.pub_key == dh.pub_key #=> false def generate_key! if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x30000000 - raise DHError, "OpenSSL::PKey::DH is immutable on OpenSSL 3.0; " \ + raise PKeyError, "OpenSSL::PKey::DH is immutable on OpenSSL 3.0; " \ "use OpenSSL::PKey.generate_key instead" end @@ -135,6 +150,9 @@ module OpenSSL::PKey end end + # Alias of PKeyError. Before version 4.0.0, this was a subclass of PKeyError. + DSAError = PKeyError + class DSA include OpenSSL::Marshal @@ -154,6 +172,18 @@ module OpenSSL::PKey OpenSSL::PKey.read(public_to_der) end + # :call-seq: + # dsa.params -> hash + # + # Stores all parameters of key to a Hash. + # + # The hash has keys 'p', 'q', 'g', 'pub_key', and 'priv_key'. + def params + %w{p q g pub_key priv_key}.map { |name| + [name, send(name)] + }.to_h + end + class << self # :call-seq: # DSA.generate(size) -> dsa @@ -218,13 +248,9 @@ module OpenSSL::PKey # sig = dsa.sign_raw(nil, digest) # p dsa.verify_raw(nil, sig, digest) #=> true def syssign(string) - q or raise OpenSSL::PKey::DSAError, "incomplete DSA" - private? or raise OpenSSL::PKey::DSAError, "Private DSA key needed!" - begin - sign_raw(nil, string) - rescue OpenSSL::PKey::PKeyError - raise OpenSSL::PKey::DSAError, $!.message - end + q or raise PKeyError, "incomplete DSA" + private? or raise PKeyError, "Private DSA key needed!" + sign_raw(nil, string) end # :call-seq: @@ -242,12 +268,13 @@ module OpenSSL::PKey # A \DSA signature value. def sysverify(digest, sig) verify_raw(nil, sig, digest) - rescue OpenSSL::PKey::PKeyError - raise OpenSSL::PKey::DSAError, $!.message end end if defined?(EC) + # Alias of PKeyError. Before version 4.0.0, this was a subclass of PKeyError. + ECError = PKeyError + class EC include OpenSSL::Marshal @@ -258,8 +285,6 @@ module OpenSSL::PKey # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw instead. def dsa_sign_asn1(data) sign_raw(nil, data) - rescue OpenSSL::PKey::PKeyError - raise OpenSSL::PKey::ECError, $!.message end # :call-seq: @@ -269,8 +294,6 @@ module OpenSSL::PKey # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw instead. def dsa_verify_asn1(data, sig) verify_raw(nil, sig, data) - rescue OpenSSL::PKey::PKeyError - raise OpenSSL::PKey::ECError, $!.message end # :call-seq: @@ -310,6 +333,9 @@ module OpenSSL::PKey end end + # Alias of PKeyError. Before version 4.0.0, this was a subclass of PKeyError. + RSAError = PKeyError + class RSA include OpenSSL::Marshal @@ -328,6 +354,18 @@ module OpenSSL::PKey OpenSSL::PKey.read(public_to_der) end + # :call-seq: + # rsa.params -> hash + # + # Stores all parameters of key to a Hash. + # + # The hash has keys 'n', 'e', 'd', 'p', 'q', 'dmp1', 'dmq1', and 'iqmp'. + def params + %w{n e d p q dmp1 dmq1 iqmp}.map { |name| + [name, send(name)] + }.to_h + end + class << self # :call-seq: # RSA.generate(size, exponent = 65537) -> RSA @@ -371,15 +409,11 @@ module OpenSSL::PKey # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw, and # PKey::PKey#verify_recover instead. def private_encrypt(string, padding = PKCS1_PADDING) - n or raise OpenSSL::PKey::RSAError, "incomplete RSA" - private? or raise OpenSSL::PKey::RSAError, "private key needed." - begin - sign_raw(nil, string, { - "rsa_padding_mode" => translate_padding_mode(padding), - }) - rescue OpenSSL::PKey::PKeyError - raise OpenSSL::PKey::RSAError, $!.message - end + n or raise PKeyError, "incomplete RSA" + private? or raise PKeyError, "private key needed." + sign_raw(nil, string, { + "rsa_padding_mode" => translate_padding_mode(padding), + }) end # :call-seq: @@ -394,14 +428,10 @@ module OpenSSL::PKey # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw, and # PKey::PKey#verify_recover instead. def public_decrypt(string, padding = PKCS1_PADDING) - n or raise OpenSSL::PKey::RSAError, "incomplete RSA" - begin - verify_recover(nil, string, { - "rsa_padding_mode" => translate_padding_mode(padding), - }) - rescue OpenSSL::PKey::PKeyError - raise OpenSSL::PKey::RSAError, $!.message - end + n or raise PKeyError, "incomplete RSA" + verify_recover(nil, string, { + "rsa_padding_mode" => translate_padding_mode(padding), + }) end # :call-seq: @@ -416,14 +446,10 @@ module OpenSSL::PKey # <b>Deprecated in version 3.0</b>. # Consider using PKey::PKey#encrypt and PKey::PKey#decrypt instead. def public_encrypt(data, padding = PKCS1_PADDING) - n or raise OpenSSL::PKey::RSAError, "incomplete RSA" - begin - encrypt(data, { - "rsa_padding_mode" => translate_padding_mode(padding), - }) - rescue OpenSSL::PKey::PKeyError - raise OpenSSL::PKey::RSAError, $!.message - end + n or raise PKeyError, "incomplete RSA" + encrypt(data, { + "rsa_padding_mode" => translate_padding_mode(padding), + }) end # :call-seq: @@ -437,15 +463,11 @@ module OpenSSL::PKey # <b>Deprecated in version 3.0</b>. # Consider using PKey::PKey#encrypt and PKey::PKey#decrypt instead. def private_decrypt(data, padding = PKCS1_PADDING) - n or raise OpenSSL::PKey::RSAError, "incomplete RSA" - private? or raise OpenSSL::PKey::RSAError, "private key needed." - begin - decrypt(data, { - "rsa_padding_mode" => translate_padding_mode(padding), - }) - rescue OpenSSL::PKey::PKeyError - raise OpenSSL::PKey::RSAError, $!.message - end + n or raise PKeyError, "incomplete RSA" + private? or raise PKeyError, "private key needed." + decrypt(data, { + "rsa_padding_mode" => translate_padding_mode(padding), + }) end PKCS1_PADDING = 1 @@ -464,7 +486,7 @@ module OpenSSL::PKey when PKCS1_OAEP_PADDING "oaep" else - raise OpenSSL::PKey::PKeyError, "unsupported padding mode" + raise PKeyError, "unsupported padding mode" end end end diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb index 75a74a3f51..3268c126b9 100644 --- a/ext/openssl/lib/openssl/ssl.rb +++ b/ext/openssl/lib/openssl/ssl.rb @@ -7,7 +7,7 @@ = Licence This program is licensed under the same licence as Ruby. - (See the file 'LICENCE'.) + (See the file 'COPYING'.) =end require "openssl/buffering" @@ -32,27 +32,7 @@ module OpenSSL }.call } - if defined?(OpenSSL::PKey::DH) - DH_ffdhe2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_ ------BEGIN DH PARAMETERS----- -MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz -+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a -87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7 -YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi -7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD -ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== ------END DH PARAMETERS----- - _end_of_pem_ - private_constant :DH_ffdhe2048 - - DEFAULT_TMP_DH_CALLBACK = lambda { |ctx, is_export, keylen| # :nodoc: - warn "using default DH parameters." if $VERBOSE - DH_ffdhe2048 - } - end - - if !(OpenSSL::OPENSSL_VERSION.start_with?("OpenSSL") && - OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10100000) + if !OpenSSL::OPENSSL_VERSION.start_with?("OpenSSL") DEFAULT_PARAMS.merge!( min_version: OpenSSL::SSL::TLS1_VERSION, ciphers: %w{ @@ -86,26 +66,13 @@ ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== AES256-SHA256 AES128-SHA AES256-SHA - }.join(":"), + }.join(":").freeze, ) end + DEFAULT_PARAMS.freeze DEFAULT_CERT_STORE = OpenSSL::X509::Store.new # :nodoc: DEFAULT_CERT_STORE.set_default_paths - DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL - - # A callback invoked when DH parameters are required for ephemeral DH key - # exchange. - # - # The callback is invoked with the SSLSocket, a - # flag indicating the use of an export cipher and the keylength - # required. - # - # The callback must return an OpenSSL::PKey::DH instance of the correct - # key length. - # - # <b>Deprecated in version 3.0.</b> Use #tmp_dh= instead. - attr_accessor :tmp_dh_callback # A callback invoked at connect time to distinguish between multiple # server names. @@ -125,7 +92,6 @@ ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== # that this form is deprecated. New applications should use #min_version= # and #max_version= as necessary. def initialize(version = nil) - self.options |= OpenSSL::SSL::OP_ALL self.ssl_version = version if version self.verify_mode = OpenSSL::SSL::VERIFY_NONE self.verify_hostname = false @@ -145,54 +111,24 @@ ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== # used. def set_params(params={}) params = DEFAULT_PARAMS.merge(params) - self.options = params.delete(:options) # set before min_version/max_version + self.options |= params.delete(:options) # set before min_version/max_version params.each{|name, value| self.__send__("#{name}=", value) } if self.verify_mode != OpenSSL::SSL::VERIFY_NONE unless self.ca_file or self.ca_path or self.cert_store - self.cert_store = DEFAULT_CERT_STORE + if not defined?(Ractor) or Ractor.current == Ractor.main + self.cert_store = DEFAULT_CERT_STORE + else + self.cert_store = Ractor.current[:__openssl_default_store__] ||= + OpenSSL::X509::Store.new.tap { |store| + store.set_default_paths + } + end end end return params end # call-seq: - # ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION - # ctx.min_version = :TLS1_2 - # ctx.min_version = nil - # - # Sets the lower bound on the supported SSL/TLS protocol version. The - # version may be specified by an integer constant named - # OpenSSL::SSL::*_VERSION, a Symbol, or +nil+ which means "any version". - # - # Be careful that you don't overwrite OpenSSL::SSL::OP_NO_{SSL,TLS}v* - # options by #options= once you have called #min_version= or - # #max_version=. - # - # === Example - # ctx = OpenSSL::SSL::SSLContext.new - # ctx.min_version = OpenSSL::SSL::TLS1_1_VERSION - # ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION - # - # sock = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx) - # sock.connect # Initiates a connection using either TLS 1.1 or TLS 1.2 - def min_version=(version) - set_minmax_proto_version(version, @max_proto_version ||= nil) - @min_proto_version = version - end - - # call-seq: - # ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION - # ctx.max_version = :TLS1_2 - # ctx.max_version = nil - # - # Sets the upper bound of the supported SSL/TLS protocol version. See - # #min_version= for the possible values. - def max_version=(version) - set_minmax_proto_version(@min_proto_version ||= nil, version) - @max_proto_version = version - end - - # call-seq: # ctx.ssl_version = :TLSv1 # ctx.ssl_version = "SSLv23" # @@ -216,8 +152,7 @@ ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== end version = METHODS_MAP[meth.intern] or raise ArgumentError, "unknown SSL method `%s'" % meth - set_minmax_proto_version(version, version) - @min_proto_version = @max_proto_version = version + self.min_version = self.max_version = version end METHODS_MAP = { @@ -459,6 +394,32 @@ ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== nil end + # Close the stream for reading. + # This method is ignored by OpenSSL as there is no reasonable way to + # implement it, but exists for compatibility with IO. + def close_read + # Unsupported and ignored. + # Just don't read any more. + end + + # Closes the stream for writing. The behavior of this method depends on + # the version of OpenSSL and the TLS protocol in use. + # + # - Sends a 'close_notify' alert to the peer. + # - Does not wait for the peer's 'close_notify' alert in response. + # + # In TLS 1.2 and earlier: + # - On receipt of a 'close_notify' alert, responds with a 'close_notify' + # alert of its own and close down the connection immediately, + # discarding any pending writes. + # + # Therefore, on TLS 1.2, this method will cause the connection to be + # completely shut down. On TLS 1.3, the connection will remain open for + # reading only. + def close_write + stop + end + private def using_anon_cipher? @@ -471,10 +432,6 @@ ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== @context.client_cert_cb end - def tmp_dh_callback - @context.tmp_dh_callback || OpenSSL::SSL::SSLContext::DEFAULT_TMP_DH_CALLBACK - end - def session_new_cb @context.session_new_cb end diff --git a/ext/openssl/lib/openssl/version.rb b/ext/openssl/lib/openssl/version.rb index 9315a79381..88570562e2 100644 --- a/ext/openssl/lib/openssl/version.rb +++ b/ext/openssl/lib/openssl/version.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true module OpenSSL - VERSION = "3.2.0" + # The version string of Ruby/OpenSSL. + VERSION = "4.0.0" end diff --git a/ext/openssl/lib/openssl/x509.rb b/ext/openssl/lib/openssl/x509.rb index f973f4f4dc..66765ffeab 100644 --- a/ext/openssl/lib/openssl/x509.rb +++ b/ext/openssl/lib/openssl/x509.rb @@ -9,7 +9,7 @@ # # = Licence # This program is licensed under the same licence as Ruby. -# (See the file 'LICENCE'.) +# (See the file 'COPYING'.) #++ require_relative 'marshal' @@ -122,8 +122,8 @@ module OpenSSL include Helpers # Get the distributionPoint fullName URI from the certificate's CRL - # distribution points extension, as described in RFC5280 Section - # 4.2.1.13 + # distribution points extension, as described in RFC 5280 Section + # 4.2.1.13. # # Returns an array of strings or nil or raises ASN1::ASN1Error. def crl_uris @@ -135,19 +135,19 @@ module OpenSSL raise ASN1::ASN1Error, "invalid extension" end - crl_uris = cdp_asn1.map do |crl_distribution_point| + crl_uris = cdp_asn1.flat_map do |crl_distribution_point| distribution_point = crl_distribution_point.value.find do |v| v.tag_class == :CONTEXT_SPECIFIC && v.tag == 0 end full_name = distribution_point&.value&.find do |v| v.tag_class == :CONTEXT_SPECIFIC && v.tag == 0 end - full_name&.value&.find do |v| + full_name&.value&.select do |v| v.tag_class == :CONTEXT_SPECIFIC && v.tag == 6 # uniformResourceIdentifier end end - crl_uris&.map(&:value) + crl_uris.empty? ? nil : crl_uris.map(&:value) end end @@ -346,6 +346,15 @@ module OpenSSL include Extension::CRLDistributionPoints include Extension::AuthorityInfoAccess + def inspect + "#<#{self.class}: " \ + "subject=#{subject.inspect}, " \ + "issuer=#{issuer.inspect}, " \ + "serial=#{serial.inspect}, " \ + "not_before=#{not_before.inspect rescue "(error)"}, " \ + "not_after=#{not_after.inspect rescue "(error)"}>" + end + def pretty_print(q) q.object_group(self) { q.breakable diff --git a/ext/openssl/openssl.gemspec b/ext/openssl/openssl.gemspec index 2765f55401..7072d599d8 100644 --- a/ext/openssl/openssl.gemspec +++ b/ext/openssl/openssl.gemspec @@ -1,19 +1,20 @@ Gem::Specification.new do |spec| spec.name = "openssl" - spec.version = "3.2.0" + spec.version = "4.0.0" spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"] spec.email = ["ruby-core@ruby-lang.org"] spec.summary = %q{SSL/TLS and general-purpose cryptography for Ruby} spec.description = %q{OpenSSL for Ruby provides access to SSL/TLS and general-purpose cryptography based on the OpenSSL library.} spec.homepage = "https://github.com/ruby/openssl" - spec.license = "Ruby" + spec.licenses = ["Ruby", "BSD-2-Clause"] if Gem::Platform === spec.platform and spec.platform =~ 'java' or RUBY_ENGINE == 'jruby' spec.platform = "java" spec.files = [] spec.add_runtime_dependency('jruby-openssl', '~> 0.14') else - spec.files = Dir["lib/**/*.rb", "ext/**/*.{c,h,rb}", "*.md", "BSDL", "LICENSE.txt"] + spec.files = Dir.glob(["lib/**/*.rb", "ext/**/*.{c,h,rb}", "*.md"], base: File.expand_path("..", __FILE__)) + + ["BSDL", "COPYING"] spec.require_paths = ["lib"] spec.extensions = ["ext/openssl/extconf.rb"] end diff --git a/ext/openssl/openssl_missing.c b/ext/openssl/openssl_missing.c deleted file mode 100644 index 4415703db4..0000000000 --- a/ext/openssl/openssl_missing.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 'OpenSSL for Ruby' project - * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> - * All rights reserved. - */ -/* - * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) - */ -#include RUBY_EXTCONF_H - -#include <string.h> /* memcpy() */ -#include <openssl/x509_vfy.h> - -#include "openssl_missing.h" - -/*** added in 1.1.0 ***/ -#if !defined(HAVE_X509_CRL_GET0_SIGNATURE) -void -ossl_X509_CRL_get0_signature(const X509_CRL *crl, const ASN1_BIT_STRING **psig, - const X509_ALGOR **palg) -{ - if (psig != NULL) - *psig = crl->signature; - if (palg != NULL) - *palg = crl->sig_alg; -} -#endif - -#if !defined(HAVE_X509_REQ_GET0_SIGNATURE) -void -ossl_X509_REQ_get0_signature(const X509_REQ *req, const ASN1_BIT_STRING **psig, - const X509_ALGOR **palg) -{ - if (psig != NULL) - *psig = req->signature; - if (palg != NULL) - *palg = req->sig_alg; -} -#endif diff --git a/ext/openssl/openssl_missing.h b/ext/openssl/openssl_missing.h index 8629bfe505..6592f9ccea 100644 --- a/ext/openssl/openssl_missing.h +++ b/ext/openssl/openssl_missing.h @@ -5,216 +5,14 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(_OSSL_OPENSSL_MISSING_H_) #define _OSSL_OPENSSL_MISSING_H_ #include "ruby/config.h" -/* added in 1.1.0 */ -#if !defined(HAVE_EVP_MD_CTX_NEW) -# define EVP_MD_CTX_new EVP_MD_CTX_create -#endif - -#if !defined(HAVE_EVP_MD_CTX_FREE) -# define EVP_MD_CTX_free EVP_MD_CTX_destroy -#endif - -#if !defined(HAVE_X509_STORE_GET_EX_DATA) -# define X509_STORE_get_ex_data(x, idx) \ - CRYPTO_get_ex_data(&(x)->ex_data, (idx)) -#endif - -#if !defined(HAVE_X509_STORE_SET_EX_DATA) -# define X509_STORE_set_ex_data(x, idx, data) \ - CRYPTO_set_ex_data(&(x)->ex_data, (idx), (data)) -#endif - -#if !defined(HAVE_X509_STORE_GET_EX_NEW_INDEX) && !defined(X509_STORE_get_ex_new_index) -# define X509_STORE_get_ex_new_index(l, p, newf, dupf, freef) \ - CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_X509_STORE, (l), (p), \ - (newf), (dupf), (freef)) -#endif - -#if !defined(HAVE_X509_CRL_GET0_SIGNATURE) -void ossl_X509_CRL_get0_signature(const X509_CRL *, const ASN1_BIT_STRING **, const X509_ALGOR **); -# define X509_CRL_get0_signature ossl_X509_CRL_get0_signature -#endif - -#if !defined(HAVE_X509_REQ_GET0_SIGNATURE) -void ossl_X509_REQ_get0_signature(const X509_REQ *, const ASN1_BIT_STRING **, const X509_ALGOR **); -# define X509_REQ_get0_signature ossl_X509_REQ_get0_signature -#endif - -#if !defined(HAVE_X509_REVOKED_GET0_SERIALNUMBER) -# define X509_REVOKED_get0_serialNumber(x) ((x)->serialNumber) -#endif - -#if !defined(HAVE_X509_REVOKED_GET0_REVOCATIONDATE) -# define X509_REVOKED_get0_revocationDate(x) ((x)->revocationDate) -#endif - -#if !defined(HAVE_X509_GET0_TBS_SIGALG) -# define X509_get0_tbs_sigalg(x) ((x)->cert_info->signature) -#endif - -#if !defined(HAVE_X509_STORE_CTX_GET0_UNTRUSTED) -# define X509_STORE_CTX_get0_untrusted(x) ((x)->untrusted) -#endif - -#if !defined(HAVE_X509_STORE_CTX_GET0_CERT) -# define X509_STORE_CTX_get0_cert(x) ((x)->cert) -#endif - -#if !defined(HAVE_X509_STORE_CTX_GET0_CHAIN) -# define X509_STORE_CTX_get0_chain(ctx) X509_STORE_CTX_get_chain(ctx) -#endif - -#if !defined(HAVE_OCSP_SINGLERESP_GET0_ID) -# define OCSP_SINGLERESP_get0_id(s) ((s)->certId) -#endif - -#if !defined(HAVE_SSL_CTX_GET_CIPHERS) -# define SSL_CTX_get_ciphers(ctx) ((ctx)->cipher_list) -#endif - -#if !defined(HAVE_X509_UP_REF) -# define X509_up_ref(x) \ - CRYPTO_add(&(x)->references, 1, CRYPTO_LOCK_X509) -#endif - -#if !defined(HAVE_X509_CRL_UP_REF) -# define X509_CRL_up_ref(x) \ - CRYPTO_add(&(x)->references, 1, CRYPTO_LOCK_X509_CRL); -#endif - -#if !defined(HAVE_X509_STORE_UP_REF) -# define X509_STORE_up_ref(x) \ - CRYPTO_add(&(x)->references, 1, CRYPTO_LOCK_X509_STORE); -#endif - -#if !defined(HAVE_SSL_SESSION_UP_REF) -# define SSL_SESSION_up_ref(x) \ - CRYPTO_add(&(x)->references, 1, CRYPTO_LOCK_SSL_SESSION); -#endif - -#if !defined(HAVE_EVP_PKEY_UP_REF) -# define EVP_PKEY_up_ref(x) \ - CRYPTO_add(&(x)->references, 1, CRYPTO_LOCK_EVP_PKEY); -#endif - -#if !defined(HAVE_OPAQUE_OPENSSL) -#define IMPL_PKEY_GETTER(_type, _name) \ -static inline _type *EVP_PKEY_get0_##_type(EVP_PKEY *pkey) { \ - return pkey->pkey._name; } -#define IMPL_KEY_ACCESSOR2(_type, _group, a1, a2, _fail_cond) \ -static inline void _type##_get0_##_group(const _type *obj, const BIGNUM **a1, const BIGNUM **a2) { \ - if (a1) *a1 = obj->a1; \ - if (a2) *a2 = obj->a2; } \ -static inline int _type##_set0_##_group(_type *obj, BIGNUM *a1, BIGNUM *a2) { \ - if (_fail_cond) return 0; \ - BN_clear_free(obj->a1); obj->a1 = a1; \ - BN_clear_free(obj->a2); obj->a2 = a2; \ - return 1; } -#define IMPL_KEY_ACCESSOR3(_type, _group, a1, a2, a3, _fail_cond) \ -static inline void _type##_get0_##_group(const _type *obj, const BIGNUM **a1, const BIGNUM **a2, const BIGNUM **a3) { \ - if (a1) *a1 = obj->a1; \ - if (a2) *a2 = obj->a2; \ - if (a3) *a3 = obj->a3; } \ -static inline int _type##_set0_##_group(_type *obj, BIGNUM *a1, BIGNUM *a2, BIGNUM *a3) { \ - if (_fail_cond) return 0; \ - BN_clear_free(obj->a1); obj->a1 = a1; \ - BN_clear_free(obj->a2); obj->a2 = a2; \ - BN_clear_free(obj->a3); obj->a3 = a3; \ - return 1; } - -#if !defined(OPENSSL_NO_RSA) -IMPL_PKEY_GETTER(RSA, rsa) -IMPL_KEY_ACCESSOR3(RSA, key, n, e, d, (n == obj->n || e == obj->e || (obj->d && d == obj->d))) -IMPL_KEY_ACCESSOR2(RSA, factors, p, q, (p == obj->p || q == obj->q)) -IMPL_KEY_ACCESSOR3(RSA, crt_params, dmp1, dmq1, iqmp, (dmp1 == obj->dmp1 || dmq1 == obj->dmq1 || iqmp == obj->iqmp)) -#endif - -#if !defined(OPENSSL_NO_DSA) -IMPL_PKEY_GETTER(DSA, dsa) -IMPL_KEY_ACCESSOR2(DSA, key, pub_key, priv_key, (pub_key == obj->pub_key || (obj->priv_key && priv_key == obj->priv_key))) -IMPL_KEY_ACCESSOR3(DSA, pqg, p, q, g, (p == obj->p || q == obj->q || g == obj->g)) -#endif - -#if !defined(OPENSSL_NO_DH) -IMPL_PKEY_GETTER(DH, dh) -IMPL_KEY_ACCESSOR2(DH, key, pub_key, priv_key, (pub_key == obj->pub_key || (obj->priv_key && priv_key == obj->priv_key))) -IMPL_KEY_ACCESSOR3(DH, pqg, p, q, g, (p == obj->p || (obj->q && q == obj->q) || g == obj->g)) -static inline ENGINE *DH_get0_engine(DH *dh) { return dh->engine; } -#endif - -#if !defined(OPENSSL_NO_EC) -IMPL_PKEY_GETTER(EC_KEY, ec) -#endif - -#undef IMPL_PKEY_GETTER -#undef IMPL_KEY_ACCESSOR2 -#undef IMPL_KEY_ACCESSOR3 -#endif /* HAVE_OPAQUE_OPENSSL */ - -#if !defined(EVP_CTRL_AEAD_GET_TAG) -# define EVP_CTRL_AEAD_GET_TAG EVP_CTRL_GCM_GET_TAG -# define EVP_CTRL_AEAD_SET_TAG EVP_CTRL_GCM_SET_TAG -# define EVP_CTRL_AEAD_SET_IVLEN EVP_CTRL_GCM_SET_IVLEN -#endif - -#if !defined(HAVE_X509_GET0_NOTBEFORE) -# define X509_get0_notBefore(x) X509_get_notBefore(x) -# define X509_get0_notAfter(x) X509_get_notAfter(x) -# define X509_CRL_get0_lastUpdate(x) X509_CRL_get_lastUpdate(x) -# define X509_CRL_get0_nextUpdate(x) X509_CRL_get_nextUpdate(x) -# define X509_set1_notBefore(x, t) X509_set_notBefore(x, t) -# define X509_set1_notAfter(x, t) X509_set_notAfter(x, t) -# define X509_CRL_set1_lastUpdate(x, t) X509_CRL_set_lastUpdate(x, t) -# define X509_CRL_set1_nextUpdate(x, t) X509_CRL_set_nextUpdate(x, t) -#endif - -#if !defined(HAVE_SSL_SESSION_GET_PROTOCOL_VERSION) -# define SSL_SESSION_get_protocol_version(s) ((s)->ssl_version) -#endif - -#if !defined(HAVE_TS_STATUS_INFO_GET0_STATUS) -# define TS_STATUS_INFO_get0_status(a) ((a)->status) -#endif - -#if !defined(HAVE_TS_STATUS_INFO_GET0_TEXT) -# define TS_STATUS_INFO_get0_text(a) ((a)->text) -#endif - -#if !defined(HAVE_TS_STATUS_INFO_GET0_FAILURE_INFO) -# define TS_STATUS_INFO_get0_failure_info(a) ((a)->failure_info) -#endif - -#if !defined(HAVE_TS_VERIFY_CTS_SET_CERTS) -# define TS_VERIFY_CTS_set_certs(ctx, crts) ((ctx)->certs=(crts)) -#endif - -#if !defined(HAVE_TS_VERIFY_CTX_SET_STORE) -# define TS_VERIFY_CTX_set_store(ctx, str) ((ctx)->store=(str)) -#endif - -#if !defined(HAVE_TS_VERIFY_CTX_ADD_FLAGS) -# define TS_VERIFY_CTX_add_flags(ctx, f) ((ctx)->flags |= (f)) -#endif - -#if !defined(HAVE_TS_RESP_CTX_SET_TIME_CB) -# define TS_RESP_CTX_set_time_cb(ctx, callback, dta) do { \ - (ctx)->time_cb = (callback); \ - (ctx)->time_cb_data = (dta); \ - } while (0) -#endif - /* added in 3.0.0 */ -#if !defined(HAVE_TS_VERIFY_CTX_SET_CERTS) -# define TS_VERIFY_CTX_set_certs(ctx, crts) TS_VERIFY_CTS_set_certs(ctx, crts) -#endif - #ifndef HAVE_EVP_MD_CTX_GET0_MD # define EVP_MD_CTX_get0_md(ctx) EVP_MD_CTX_md(ctx) #endif @@ -224,11 +22,7 @@ IMPL_PKEY_GETTER(EC_KEY, ec) * EVP_MD_CTX_get_pkey_ctx(x) in OpenSSL 3.0. */ #ifndef HAVE_EVP_MD_CTX_GET_PKEY_CTX -# ifdef HAVE_EVP_MD_CTX_PKEY_CTX # define EVP_MD_CTX_get_pkey_ctx(x) EVP_MD_CTX_pkey_ctx(x) -# else -# define EVP_MD_CTX_get_pkey_ctx(x) (x)->pctx -# endif #endif #ifndef HAVE_EVP_PKEY_EQ diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index 00eded55cb..98127fcba0 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -5,93 +5,79 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" #include <stdarg.h> /* for ossl_raise */ -/* OpenSSL >= 1.1.0 and LibreSSL >= 2.9.0 */ -#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER >= 0x10100000 -# define HAVE_OPENSSL_110_THREADING_API -#else -# include <ruby/thread_native.h> -#endif - /* * Data Conversion */ -#define OSSL_IMPL_ARY2SK(name, type, expected_class, dup) \ -VALUE \ -ossl_##name##_ary2sk0(VALUE ary) \ -{ \ - STACK_OF(type) *sk; \ - VALUE val; \ - type *x; \ - int i; \ - \ - Check_Type(ary, T_ARRAY); \ - sk = sk_##type##_new_null(); \ - if (!sk) ossl_raise(eOSSLError, NULL); \ - \ - for (i = 0; i < RARRAY_LEN(ary); i++) { \ - val = rb_ary_entry(ary, i); \ - if (!rb_obj_is_kind_of(val, expected_class)) { \ - sk_##type##_pop_free(sk, type##_free); \ - ossl_raise(eOSSLError, "object in array not" \ - " of class ##type##"); \ - } \ - x = dup(val); /* NEED TO DUP */ \ - sk_##type##_push(sk, x); \ - } \ - return (VALUE)sk; \ -} \ - \ -STACK_OF(type) * \ -ossl_protect_##name##_ary2sk(VALUE ary, int *status) \ -{ \ - return (STACK_OF(type)*)rb_protect( \ - (VALUE (*)(VALUE))ossl_##name##_ary2sk0, \ - ary, \ - status); \ -} \ - \ -STACK_OF(type) * \ -ossl_##name##_ary2sk(VALUE ary) \ -{ \ - STACK_OF(type) *sk; \ - int status = 0; \ - \ - sk = ossl_protect_##name##_ary2sk(ary, &status); \ - if (status) rb_jump_tag(status); \ - \ - return sk; \ +#define OSSL_IMPL_ARY2SK(name, type, expected_class, dup) \ +VALUE \ +ossl_##name##_ary2sk0(VALUE ary) \ +{ \ + STACK_OF(type) *sk; \ + VALUE val; \ + type *x; \ + int i; \ + \ + Check_Type(ary, T_ARRAY); \ + sk = sk_##type##_new_null(); \ + if (!sk) ossl_raise(eOSSLError, NULL); \ + \ + for (i = 0; i < RARRAY_LEN(ary); i++) { \ + val = rb_ary_entry(ary, i); \ + if (!rb_obj_is_kind_of(val, expected_class)) { \ + sk_##type##_pop_free(sk, type##_free); \ + ossl_raise(eOSSLError, "object in array not" \ + " of class ##type##"); \ + } \ + x = dup(val); /* NEED TO DUP */ \ + sk_##type##_push(sk, x); \ + } \ + return (VALUE)sk; \ +} \ + \ +STACK_OF(type) * \ +ossl_protect_##name##_ary2sk(VALUE ary, int *status) \ +{ \ + return (STACK_OF(type)*)rb_protect( \ + (VALUE (*)(VALUE))ossl_##name##_ary2sk0, \ + ary, \ + status); \ +} \ + \ +STACK_OF(type) * \ +ossl_##name##_ary2sk(VALUE ary) \ +{ \ + STACK_OF(type) *sk; \ + int status = 0; \ + \ + sk = ossl_protect_##name##_ary2sk(ary, &status); \ + if (status) rb_jump_tag(status); \ + \ + return sk; \ } OSSL_IMPL_ARY2SK(x509, X509, cX509Cert, DupX509CertPtr) -#define OSSL_IMPL_SK2ARY(name, type) \ -VALUE \ -ossl_##name##_sk2ary(const STACK_OF(type) *sk) \ -{ \ - type *t; \ - int i, num; \ - VALUE ary; \ - \ - if (!sk) { \ - OSSL_Debug("empty sk!"); \ - return Qnil; \ - } \ - num = sk_##type##_num(sk); \ - if (num < 0) { \ - OSSL_Debug("items in sk < -1???"); \ - return rb_ary_new(); \ - } \ - ary = rb_ary_new2(num); \ - \ - for (i=0; i<num; i++) { \ - t = sk_##type##_value(sk, i); \ - rb_ary_push(ary, ossl_##name##_new(t)); \ - } \ - return ary; \ +#define OSSL_IMPL_SK2ARY(name, type) \ +VALUE \ +ossl_##name##_sk2ary(const STACK_OF(type) *sk) \ +{ \ + type *t; \ + int i, num; \ + VALUE ary; \ + \ + RUBY_ASSERT(sk != NULL); \ + num = sk_##type##_num(sk); \ + ary = rb_ary_new_capa(num); \ + \ + for (i=0; i<num; i++) { \ + t = sk_##type##_value(sk, i); \ + rb_ary_push(ary, ossl_##name##_new(t)); \ + } \ + return ary; \ } OSSL_IMPL_SK2ARY(x509, X509) OSSL_IMPL_SK2ARY(x509crl, X509_CRL) @@ -111,14 +97,14 @@ ossl_str_new(const char *ptr, long len, int *pstate) str = rb_protect(ossl_str_new_i, len, &state); if (pstate) - *pstate = state; + *pstate = state; if (state) { - if (!pstate) - rb_set_errinfo(Qnil); - return Qnil; + if (!pstate) + rb_set_errinfo(Qnil); + return Qnil; } if (ptr) - memcpy(RSTRING_PTR(str), ptr, len); + memcpy(RSTRING_PTR(str), ptr, len); return str; } @@ -131,22 +117,22 @@ ossl_buf2str(char *buf, int len) str = ossl_str_new(buf, len, &state); OPENSSL_free(buf); if (state) - rb_jump_tag(state); + rb_jump_tag(state); return str; } void -ossl_bin2hex(unsigned char *in, char *out, size_t inlen) +ossl_bin2hex(const unsigned char *in, char *out, size_t inlen) { const char *hex = "0123456789abcdef"; size_t i; assert(inlen <= LONG_MAX / 2); for (i = 0; i < inlen; i++) { - unsigned char p = in[i]; + unsigned char p = in[i]; - out[i * 2 + 0] = hex[p >> 4]; - out[i * 2 + 1] = hex[p & 0x0f]; + out[i * 2 + 0] = hex[p >> 4]; + out[i * 2 + 1] = hex[p & 0x0f]; } } @@ -157,14 +143,14 @@ VALUE ossl_pem_passwd_value(VALUE pass) { if (NIL_P(pass)) - return Qnil; + return Qnil; StringValue(pass); /* PEM_BUFSIZE is currently used as the second argument of pem_password_cb, * that is +max_len+ of ossl_pem_passwd_cb() */ if (RSTRING_LEN(pass) > PEM_BUFSIZE) - ossl_raise(eOSSLError, "password must not be longer than %d bytes", PEM_BUFSIZE); + ossl_raise(eOSSLError, "password must not be longer than %d bytes", PEM_BUFSIZE); return pass; } @@ -174,7 +160,7 @@ ossl_pem_passwd_cb0(VALUE flag) { VALUE pass = rb_yield(flag); if (NIL_P(pass)) - return Qnil; + return Qnil; StringValue(pass); return pass; } @@ -187,46 +173,46 @@ ossl_pem_passwd_cb(char *buf, int max_len, int flag, void *pwd_) VALUE rflag, pass = (VALUE)pwd_; if (RTEST(pass)) { - /* PEM_def_callback(buf, max_len, flag, StringValueCStr(pass)) does not - * work because it does not allow NUL characters and truncates to 1024 - * bytes silently if the input is over 1024 bytes */ - if (RB_TYPE_P(pass, T_STRING)) { - len = RSTRING_LEN(pass); - if (len <= max_len) { - memcpy(buf, RSTRING_PTR(pass), len); - return (int)len; - } - } - OSSL_Debug("passed data is not valid String???"); - return -1; + /* PEM_def_callback(buf, max_len, flag, StringValueCStr(pass)) does not + * work because it does not allow NUL characters and truncates to 1024 + * bytes silently if the input is over 1024 bytes */ + if (RB_TYPE_P(pass, T_STRING)) { + len = RSTRING_LEN(pass); + if (len <= max_len) { + memcpy(buf, RSTRING_PTR(pass), len); + return (int)len; + } + } + OSSL_Debug("passed data is not valid String???"); + return -1; } if (!rb_block_given_p()) { - return PEM_def_callback(buf, max_len, flag, NULL); + return PEM_def_callback(buf, max_len, flag, NULL); } while (1) { - /* - * when the flag is nonzero, this password - * will be used to perform encryption; otherwise it will - * be used to perform decryption. - */ - rflag = flag ? Qtrue : Qfalse; - pass = rb_protect(ossl_pem_passwd_cb0, rflag, &status); - if (status) { - /* ignore an exception raised. */ - rb_set_errinfo(Qnil); - return -1; - } - if (NIL_P(pass)) - return -1; - len = RSTRING_LEN(pass); - if (len > max_len) { - rb_warning("password must not be longer than %d bytes", max_len); - continue; - } - memcpy(buf, RSTRING_PTR(pass), len); - break; + /* + * when the flag is nonzero, this password + * will be used to perform encryption; otherwise it will + * be used to perform decryption. + */ + rflag = flag ? Qtrue : Qfalse; + pass = rb_protect(ossl_pem_passwd_cb0, rflag, &status); + if (status) { + /* ignore an exception raised. */ + rb_set_errinfo(Qnil); + return -1; + } + if (NIL_P(pass)) + return -1; + len = RSTRING_LEN(pass); + if (len > max_len) { + rb_warning("password must not be longer than %d bytes", max_len); + continue; + } + memcpy(buf, RSTRING_PTR(pass), len); + break; } return (int)len; } @@ -261,19 +247,24 @@ VALUE ossl_to_der_if_possible(VALUE obj) { if(rb_respond_to(obj, ossl_s_to_der)) - return ossl_to_der(obj); + return ossl_to_der(obj); return obj; } /* * Errors */ +static ID id_i_errors; + +static void collect_errors_into(VALUE ary); + VALUE ossl_make_error(VALUE exc, VALUE str) { unsigned long e; const char *data; int flags; + VALUE errors = rb_ary_new(); if (NIL_P(str)) str = rb_str_new(NULL, 0); @@ -290,10 +281,12 @@ ossl_make_error(VALUE exc, VALUE str) rb_str_cat_cstr(str, msg ? msg : "(null)"); if (flags & ERR_TXT_STRING && data) rb_str_catf(str, " (%s)", data); - ossl_clear_error(); + collect_errors_into(errors); } - return rb_exc_new_str(exc, str); + VALUE obj = rb_exc_new_str(exc, str); + rb_ivar_set(obj, id_i_errors, errors); + return obj; } void @@ -303,24 +296,23 @@ ossl_raise(VALUE exc, const char *fmt, ...) VALUE err; if (fmt) { - va_start(args, fmt); - err = rb_vsprintf(fmt, args); - va_end(args); + va_start(args, fmt); + err = rb_vsprintf(fmt, args); + va_end(args); } else { - err = Qnil; + err = Qnil; } rb_exc_raise(ossl_make_error(exc, err)); } -void -ossl_clear_error(void) +static void +collect_errors_into(VALUE ary) { - if (dOSSL == Qtrue) { + if (dOSSL == Qtrue || !NIL_P(ary)) { unsigned long e; const char *file, *data, *func, *lib, *reason; - char append[256] = ""; int line, flags; #ifdef HAVE_ERR_GET_ERROR_ALL @@ -332,13 +324,18 @@ ossl_clear_error(void) lib = ERR_lib_error_string(e); reason = ERR_reason_error_string(e); + VALUE str = rb_sprintf("error:%08lX:%s:%s:%s", e, lib ? lib : "", + func ? func : "", reason ? reason : ""); if (flags & ERR_TXT_STRING) { if (!data) data = "(null)"; - snprintf(append, sizeof(append), " (%s)", data); + rb_str_catf(str, " (%s)", data); } - rb_warn("error on stack: error:%08lX:%s:%s:%s%s", e, lib ? lib : "", - func ? func : "", reason ? reason : "", append); + + if (dOSSL == Qtrue) + rb_warn("error on stack: %"PRIsVALUE, str); + if (!NIL_P(ary)) + rb_ary_push(ary, str); } } else { @@ -346,16 +343,61 @@ ossl_clear_error(void) } } +void +ossl_clear_error(void) +{ + collect_errors_into(Qnil); +} + +/* + * call-seq: + * ossl_error.detailed_message(**) -> string + * + * Returns the exception message decorated with the captured \OpenSSL error + * queue entries. + */ +static VALUE +osslerror_detailed_message(int argc, VALUE *argv, VALUE self) +{ + VALUE str; +#ifdef HAVE_RB_CALL_SUPER_KW + // Ruby >= 3.2 + if (RTEST(rb_funcall(rb_eException, rb_intern("method_defined?"), 1, + ID2SYM(rb_intern("detailed_message"))))) + str = rb_call_super_kw(argc, argv, RB_PASS_CALLED_KEYWORDS); + else +#endif + str = rb_funcall(self, rb_intern("message"), 0); + VALUE errors = rb_attr_get(self, id_i_errors); + + // OpenSSLError was not created by ossl_make_error() + if (!RB_TYPE_P(errors, T_ARRAY)) + return str; + + str = rb_str_resurrect(str); + rb_str_catf(str, "\nOpenSSL error queue reported %ld errors:", + RARRAY_LEN(errors)); + for (long i = 0; i < RARRAY_LEN(errors); i++) { + VALUE err = RARRAY_AREF(errors, i); + rb_str_catf(str, "\n%"PRIsVALUE, err); + } + return str; +} + /* * call-seq: * OpenSSL.errors -> [String...] * - * See any remaining errors held in queue. + * Returns any remaining errors held in the \OpenSSL thread-local error queue + * and clears the queue. This should normally return an empty array. + * + * This is intended for debugging Ruby/OpenSSL. If you see any errors here, + * it likely indicates a bug in the extension. Please file an issue at + * https://github.com/ruby/openssl. * - * Any errors you see here are probably due to a bug in Ruby's OpenSSL - * implementation. + * For debugging your program, OpenSSL.debug= may be useful. */ -VALUE +static VALUE ossl_get_errors(VALUE _) { VALUE ary; @@ -377,6 +419,8 @@ VALUE dOSSL; /* * call-seq: * OpenSSL.debug -> true | false + * + * Returns whether Ruby/OpenSSL's debug mode is currently enabled. */ static VALUE ossl_debug_get(VALUE self) @@ -386,9 +430,9 @@ ossl_debug_get(VALUE self) /* * call-seq: - * OpenSSL.debug = boolean -> boolean + * OpenSSL.debug = boolean * - * Turns on or off debug mode. With debug mode, all errors added to the OpenSSL + * Turns on or off debug mode. With debug mode, all errors added to the \OpenSSL * error queue will be printed to stderr. */ static VALUE @@ -402,6 +446,8 @@ ossl_debug_set(VALUE self, VALUE val) /* * call-seq: * OpenSSL.fips_mode -> true | false + * + * Returns whether the FIPS mode is currently enabled. */ static VALUE ossl_fips_mode_get(VALUE self) @@ -411,7 +457,7 @@ ossl_fips_mode_get(VALUE self) VALUE enabled; enabled = EVP_default_properties_is_fips_enabled(NULL) ? Qtrue : Qfalse; return enabled; -#elif defined(OPENSSL_FIPS) +#elif defined(OPENSSL_FIPS) || defined(OPENSSL_IS_AWSLC) VALUE enabled; enabled = FIPS_mode() ? Qtrue : Qfalse; return enabled; @@ -422,10 +468,10 @@ ossl_fips_mode_get(VALUE self) /* * call-seq: - * OpenSSL.fips_mode = boolean -> boolean + * OpenSSL.fips_mode = boolean * * Turns FIPS mode on or off. Turning on FIPS mode will obviously only have an - * effect for FIPS-capable installations of the OpenSSL library. Trying to do + * effect for FIPS-capable installations of the \OpenSSL library. Trying to do * so otherwise will result in an error. * * === Examples @@ -446,123 +492,32 @@ ossl_fips_mode_set(VALUE self, VALUE enabled) } } return enabled; -#elif defined(OPENSSL_FIPS) +#elif defined(OPENSSL_FIPS) || defined(OPENSSL_IS_AWSLC) if (RTEST(enabled)) { - int mode = FIPS_mode(); - if(!mode && !FIPS_mode_set(1)) /* turning on twice leads to an error */ - ossl_raise(eOSSLError, "Turning on FIPS mode failed"); + int mode = FIPS_mode(); + if(!mode && !FIPS_mode_set(1)) /* turning on twice leads to an error */ + ossl_raise(eOSSLError, "Turning on FIPS mode failed"); } else { - if(!FIPS_mode_set(0)) /* turning off twice is OK */ - ossl_raise(eOSSLError, "Turning off FIPS mode failed"); + if(!FIPS_mode_set(0)) /* turning off twice is OK */ + ossl_raise(eOSSLError, "Turning off FIPS mode failed"); } return enabled; #else if (RTEST(enabled)) - ossl_raise(eOSSLError, "This version of OpenSSL does not support FIPS mode"); + ossl_raise(eOSSLError, "This version of OpenSSL does not support FIPS mode"); return enabled; #endif } -#if !defined(HAVE_OPENSSL_110_THREADING_API) -/** - * Stores locks needed for OpenSSL thread safety - */ -struct CRYPTO_dynlock_value { - rb_nativethread_lock_t lock; - rb_nativethread_id_t owner; - size_t count; -}; - -static void -ossl_lock_init(struct CRYPTO_dynlock_value *l) -{ - rb_nativethread_lock_initialize(&l->lock); - l->count = 0; -} - -static void -ossl_lock_unlock(int mode, struct CRYPTO_dynlock_value *l) -{ - if (mode & CRYPTO_LOCK) { - /* TODO: rb_nativethread_id_t is not necessarily compared with ==. */ - rb_nativethread_id_t tid = rb_nativethread_self(); - if (l->count && l->owner == tid) { - l->count++; - return; - } - rb_nativethread_lock_lock(&l->lock); - l->owner = tid; - l->count = 1; - } else { - if (!--l->count) - rb_nativethread_lock_unlock(&l->lock); - } -} - -static struct CRYPTO_dynlock_value * -ossl_dyn_create_callback(const char *file, int line) -{ - /* Do not use xmalloc() here, since it may raise NoMemoryError */ - struct CRYPTO_dynlock_value *dynlock = - OPENSSL_malloc(sizeof(struct CRYPTO_dynlock_value)); - if (dynlock) - ossl_lock_init(dynlock); - return dynlock; -} - -static void -ossl_dyn_lock_callback(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line) -{ - ossl_lock_unlock(mode, l); -} - -static void -ossl_dyn_destroy_callback(struct CRYPTO_dynlock_value *l, const char *file, int line) -{ - rb_nativethread_lock_destroy(&l->lock); - OPENSSL_free(l); -} - -static void ossl_threadid_func(CRYPTO_THREADID *id) -{ - /* register native thread id */ - CRYPTO_THREADID_set_pointer(id, (void *)rb_nativethread_self()); -} - -static struct CRYPTO_dynlock_value *ossl_locks; - -static void -ossl_lock_callback(int mode, int type, const char *file, int line) -{ - ossl_lock_unlock(mode, &ossl_locks[type]); -} - -static void Init_ossl_locks(void) -{ - int i; - int num_locks = CRYPTO_num_locks(); - - ossl_locks = ALLOC_N(struct CRYPTO_dynlock_value, num_locks); - for (i = 0; i < num_locks; i++) - ossl_lock_init(&ossl_locks[i]); - - CRYPTO_THREADID_set_callback(ossl_threadid_func); - CRYPTO_set_locking_callback(ossl_lock_callback); - CRYPTO_set_dynlock_create_callback(ossl_dyn_create_callback); - CRYPTO_set_dynlock_lock_callback(ossl_dyn_lock_callback); - CRYPTO_set_dynlock_destroy_callback(ossl_dyn_destroy_callback); -} -#endif /* !HAVE_OPENSSL_110_THREADING_API */ - /* * call-seq: - * OpenSSL.fixed_length_secure_compare(string, string) -> boolean + * OpenSSL.fixed_length_secure_compare(string, string) -> true or false * * Constant time memory comparison for fixed length strings, such as results - * of HMAC calculations. + * of \HMAC calculations. * * Returns +true+ if the strings are identical, +false+ if they are of the same - * length but not identical. If the length is different, +ArgumentError+ is + * length but not identical. If the length is different, ArgumentError is * raised. */ static VALUE @@ -578,13 +533,13 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) } switch (CRYPTO_memcmp(p1, p2, len1)) { - case 0: return Qtrue; - default: return Qfalse; + case 0: return Qtrue; + default: return Qfalse; } } /* - * OpenSSL provides SSL, TLS and general purpose cryptography. It wraps the + * OpenSSL provides \SSL, TLS and general purpose cryptography. It wraps the * OpenSSL[https://www.openssl.org/] library. * * = Examples @@ -639,7 +594,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * * === Loading an Encrypted Key * - * OpenSSL will prompt you for your password when loading an encrypted key. + * \OpenSSL will prompt you for your password when loading an encrypted key. * If you will not be able to type in the password you may provide it when * loading the key: * @@ -702,7 +657,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * * == PBKDF2 Password-based Encryption * - * If supported by the underlying OpenSSL version used, Password-based + * If supported by the underlying \OpenSSL version used, Password-based * Encryption should use the features of PKCS5. If not supported or if * required by legacy applications, the older, less secure methods specified * in RFC 2898 are also supported (see below). @@ -761,7 +716,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * decrypted = cipher.update encrypted * decrypted << cipher.final * - * == X509 Certificates + * == \X509 Certificates * * === Creating a Certificate * @@ -798,7 +753,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * extension_factory.create_extension('subjectKeyIdentifier', 'hash') * * The list of supported extensions (and in some cases their possible values) - * can be derived from the "objects.h" file in the OpenSSL source code. + * can be derived from the "objects.h" file in the \OpenSSL source code. * * === Signing a Certificate * @@ -952,23 +907,23 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * io.write csr_cert.to_pem * end * - * == SSL and TLS Connections + * == \SSL and TLS Connections * - * Using our created key and certificate we can create an SSL or TLS connection. - * An SSLContext is used to set up an SSL session. + * Using our created key and certificate we can create an \SSL or TLS + * connection. An OpenSSL::SSL::SSLContext is used to set up an \SSL session. * * context = OpenSSL::SSL::SSLContext.new * - * === SSL Server + * === \SSL Server * - * An SSL server requires the certificate and private key to communicate + * An \SSL server requires the certificate and private key to communicate * securely with its clients: * * context.cert = cert * context.key = key * - * Then create an SSLServer with a TCP server socket and the context. Use the - * SSLServer like an ordinary TCP server. + * Then create an OpenSSL::SSL::SSLServer with a TCP server socket and the + * context. Use the SSLServer like an ordinary TCP server. * * require 'socket' * @@ -987,14 +942,15 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * ssl_connection.close * end * - * === SSL client + * === \SSL client * - * An SSL client is created with a TCP socket and the context. - * SSLSocket#connect must be called to initiate the SSL handshake and start - * encryption. A key and certificate are not required for the client socket. + * An \SSL client is created with a TCP socket and the context. + * OpenSSL::SSL::SSLSocket#connect must be called to initiate the \SSL handshake + * and start encryption. A key and certificate are not required for the client + * socket. * - * Note that SSLSocket#close doesn't close the underlying socket by default. Set - * SSLSocket#sync_close to true if you want. + * Note that OpenSSL::SSL::SSLSocket#close doesn't close the underlying socket + * by default. Set OpenSSL::SSL::SSLSocket#sync_close to true if you want. * * require 'socket' * @@ -1010,7 +966,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * * === Peer Verification * - * An unverified SSL connection does not provide much security. For enhanced + * An unverified \SSL connection does not provide much security. For enhanced * security the client or server can verify the certificate of its peer. * * The client can be modified to verify the server's certificate against the @@ -1050,15 +1006,8 @@ Init_openssl(void) /* * Init all digests, ciphers */ -#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000 if (!OPENSSL_init_ssl(0, NULL)) rb_raise(rb_eRuntimeError, "OPENSSL_init_ssl"); -#else - OpenSSL_add_ssl_algorithms(); - OpenSSL_add_all_algorithms(); - ERR_load_crypto_strings(); - SSL_load_error_strings(); -#endif /* * Init main module @@ -1068,26 +1017,34 @@ Init_openssl(void) rb_define_singleton_method(mOSSL, "fixed_length_secure_compare", ossl_crypto_fixed_length_secure_compare, 2); /* - * Version of OpenSSL the ruby OpenSSL extension was built with + * \OpenSSL library version string used to compile the Ruby/OpenSSL + * extension. This may differ from the version used at runtime. */ - rb_define_const(mOSSL, "OPENSSL_VERSION", rb_str_new2(OPENSSL_VERSION_TEXT)); + rb_define_const(mOSSL, "OPENSSL_VERSION", + rb_obj_freeze(rb_str_new_cstr(OPENSSL_VERSION_TEXT))); /* - * Version of OpenSSL the ruby OpenSSL extension is running with + * \OpenSSL library version string currently used at runtime. */ -#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000 - rb_define_const(mOSSL, "OPENSSL_LIBRARY_VERSION", rb_str_new2(OpenSSL_version(OPENSSL_VERSION))); -#else - rb_define_const(mOSSL, "OPENSSL_LIBRARY_VERSION", rb_str_new2(SSLeay_version(SSLEAY_VERSION))); -#endif + rb_define_const( + mOSSL, + "OPENSSL_LIBRARY_VERSION", + rb_obj_freeze(rb_str_new_cstr(OpenSSL_version(OPENSSL_VERSION))) + ); /* - * Version number of OpenSSL the ruby OpenSSL extension was built with - * (base 16). The formats are below. + * \OpenSSL library version number used to compile the Ruby/OpenSSL + * extension. This may differ from the version used at runtime. + * + * The version number is encoded into a single integer value. The number + * follows the format: * - * [OpenSSL 3] <tt>0xMNN00PP0 (major minor 00 patch 0)</tt> - * [OpenSSL before 3] <tt>0xMNNFFPPS (major minor fix patch status)</tt> - * [LibreSSL] <tt>0x20000000 (fixed value)</tt> + * [\OpenSSL 3.0.0 or later] + * <tt>0xMNN00PP0</tt> (major minor 00 patch 0) + * [\OpenSSL 1.1.1 or earlier] + * <tt>0xMNNFFPPS</tt> (major minor fix patch status) + * [LibreSSL] + * <tt>0x20000000</tt> (a fixed value) * * See also the man page OPENSSL_VERSION_NUMBER(3). */ @@ -1095,9 +1052,12 @@ Init_openssl(void) #if defined(LIBRESSL_VERSION_NUMBER) /* - * Version number of LibreSSL the ruby OpenSSL extension was built with - * (base 16). The format is <tt>0xMNNFF00f (major minor fix 00 - * status)</tt>. This constant is only defined in LibreSSL cases. + * LibreSSL library version number used to compile the Ruby/OpenSSL + * extension. This may differ from the version used at runtime. + * + * This constant is only defined if the extension was compiled against + * LibreSSL. The number follows the format: + * <tt>0xMNNFF00f</tt> (major minor fix 00 status). * * See also the man page LIBRESSL_VERSION_NUMBER(3). */ @@ -1105,28 +1065,50 @@ Init_openssl(void) #endif /* - * Boolean indicating whether OpenSSL is FIPS-capable or not + * Boolean indicating whether the \OpenSSL library is FIPS-capable or not. + * Always <tt>true</tt> for \OpenSSL 3.0 and later. + * + * This is obsolete and will be removed in the future. + * See also OpenSSL.fips_mode. */ rb_define_const(mOSSL, "OPENSSL_FIPS", /* OpenSSL 3 is FIPS-capable even when it is installed without fips option */ #if OSSL_OPENSSL_PREREQ(3, 0, 0) Qtrue #elif defined(OPENSSL_FIPS) - Qtrue + Qtrue +#elif defined(OPENSSL_IS_AWSLC) // AWS-LC FIPS can only be enabled during compile time. + FIPS_mode() ? Qtrue : Qfalse #else - Qfalse + Qfalse #endif - ); + ); rb_define_module_function(mOSSL, "fips_mode", ossl_fips_mode_get, 0); rb_define_module_function(mOSSL, "fips_mode=", ossl_fips_mode_set, 1); rb_global_variable(&eOSSLError); /* - * Generic error, - * common for all classes under OpenSSL module + * Generic error class for OpenSSL. All error classes in this library + * inherit from this class. + * + * This class indicates that an error was reported by the underlying + * \OpenSSL library. + */ + eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); + /* + * \OpenSSL error queue entries captured at the time the exception was + * raised. The same information is printed to stderr if OpenSSL.debug is + * set to +true+. + * + * This is an array of zero or more strings, ordered from the oldest to the + * newest. The format of the strings is not stable and may vary across + * versions of \OpenSSL or versions of this Ruby extension. + * + * See also the man page ERR_get_error(3). */ - eOSSLError = rb_define_class_under(mOSSL,"OpenSSLError",rb_eStandardError); + rb_attr(eOSSLError, rb_intern_const("errors"), 1, 0, 0); + rb_define_method(eOSSLError, "detailed_message", osslerror_detailed_message, -1); /* * Init debug core @@ -1142,32 +1124,27 @@ Init_openssl(void) * Get ID of to_der */ ossl_s_to_der = rb_intern("to_der"); - -#if !defined(HAVE_OPENSSL_110_THREADING_API) - Init_ossl_locks(); -#endif + id_i_errors = rb_intern("@errors"); /* * Init components */ + Init_ossl_asn1(); Init_ossl_bn(); Init_ossl_cipher(); Init_ossl_config(); Init_ossl_digest(); + Init_ossl_engine(); Init_ossl_hmac(); + Init_ossl_kdf(); Init_ossl_ns_spki(); + Init_ossl_ocsp(); Init_ossl_pkcs12(); Init_ossl_pkcs7(); Init_ossl_pkey(); + Init_ossl_provider(); Init_ossl_rand(); Init_ossl_ssl(); -#ifndef OPENSSL_NO_TS Init_ossl_ts(); -#endif Init_ossl_x509(); - Init_ossl_ocsp(); - Init_ossl_engine(); - Init_ossl_provider(); - Init_ossl_asn1(); - Init_ossl_kdf(); } diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index 68d42b71e2..0b479a7200 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(_OSSL_H_) #define _OSSL_H_ @@ -17,6 +17,12 @@ #include <errno.h> #include <ruby/io.h> #include <ruby/thread.h> +#ifdef HAVE_RUBY_RACTOR_H +#include <ruby/ractor.h> +#else +#define RUBY_TYPED_FROZEN_SHAREABLE 0 +#endif + #include <openssl/opensslv.h> #include <openssl/err.h> @@ -39,6 +45,7 @@ #include <openssl/dsa.h> #include <openssl/evp.h> #include <openssl/dh.h> +#include "openssl_missing.h" #ifndef LIBRESSL_VERSION_NUMBER # define OSSL_IS_LIBRESSL 0 @@ -64,6 +71,11 @@ #if OSSL_OPENSSL_PREREQ(3, 0, 0) # define OSSL_USE_PROVIDER +# include <openssl/provider.h> +#endif + +#if OSSL_OPENSSL_PREREQ(3, 0, 0) +# define OSSL_HAVE_IMMUTABLE_PKEY #endif /* @@ -80,10 +92,10 @@ extern VALUE eOSSLError; * CheckTypes */ #define OSSL_Check_Kind(obj, klass) do {\ - if (!rb_obj_is_kind_of((obj), (klass))) {\ - ossl_raise(rb_eTypeError, "wrong argument (%"PRIsVALUE")! (Expected kind of %"PRIsVALUE")",\ - rb_obj_class(obj), (klass));\ - }\ + if (!rb_obj_is_kind_of((obj), (klass))) {\ + ossl_raise(rb_eTypeError, "wrong argument (%"PRIsVALUE")! (Expected kind of %"PRIsVALUE")",\ + rb_obj_class(obj), (klass));\ + }\ } while (0) /* @@ -119,7 +131,7 @@ do{\ * Convert binary string to hex string. The caller is responsible for * ensuring out has (2 * len) bytes of capacity. */ -void ossl_bin2hex(unsigned char *in, char *out, size_t len); +void ossl_bin2hex(const unsigned char *in, char *out, size_t len); /* * Our default PEM callback @@ -162,38 +174,35 @@ VALUE ossl_to_der_if_possible(VALUE); extern VALUE dOSSL; #define OSSL_Debug(...) do { \ - if (dOSSL == Qtrue) { \ - fprintf(stderr, "OSSL_DEBUG: "); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, " [%s:%d]\n", __FILE__, __LINE__); \ - } \ + if (dOSSL == Qtrue) { \ + fprintf(stderr, "OSSL_DEBUG: "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, " [%s:%d]\n", __FILE__, __LINE__); \ + } \ } while (0) /* * Include all parts */ -#include "openssl_missing.h" #include "ossl_asn1.h" #include "ossl_bio.h" #include "ossl_bn.h" #include "ossl_cipher.h" #include "ossl_config.h" #include "ossl_digest.h" +#include "ossl_engine.h" #include "ossl_hmac.h" +#include "ossl_kdf.h" #include "ossl_ns_spki.h" #include "ossl_ocsp.h" #include "ossl_pkcs12.h" #include "ossl_pkcs7.h" #include "ossl_pkey.h" +#include "ossl_provider.h" #include "ossl_rand.h" #include "ossl_ssl.h" -#ifndef OPENSSL_NO_TS - #include "ossl_ts.h" -#endif +#include "ossl_ts.h" #include "ossl_x509.h" -#include "ossl_engine.h" -#include "ossl_provider.h" -#include "ossl_kdf.h" void Init_openssl(void); diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 71c452c88a..71a87f0463 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -5,68 +5,85 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" -static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset, - int depth, int yield, long *num_read); -static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self); +/********/ +/* + * ASN1 module + */ +#define ossl_asn1_get_value(o) rb_attr_get((o),sivVALUE) +#define ossl_asn1_get_tag(o) rb_attr_get((o),sivTAG) +#define ossl_asn1_get_tagging(o) rb_attr_get((o),sivTAGGING) +#define ossl_asn1_get_tag_class(o) rb_attr_get((o),sivTAG_CLASS) +#define ossl_asn1_get_indefinite_length(o) rb_attr_get((o),sivINDEFINITE_LENGTH) + +#define ossl_asn1_set_value(o,v) rb_ivar_set((o),sivVALUE,(v)) +#define ossl_asn1_set_tag(o,v) rb_ivar_set((o),sivTAG,(v)) +#define ossl_asn1_set_tagging(o,v) rb_ivar_set((o),sivTAGGING,(v)) +#define ossl_asn1_set_tag_class(o,v) rb_ivar_set((o),sivTAG_CLASS,(v)) +#define ossl_asn1_set_indefinite_length(o,v) rb_ivar_set((o),sivINDEFINITE_LENGTH,(v)) + +VALUE mASN1; +static VALUE eASN1Error; + +VALUE cASN1Data; +static VALUE cASN1Primitive; +static VALUE cASN1Constructive; + +static VALUE cASN1EndOfContent; +static VALUE cASN1Boolean; /* BOOLEAN */ +static VALUE cASN1Integer, cASN1Enumerated; /* INTEGER */ +static VALUE cASN1BitString; /* BIT STRING */ +static VALUE cASN1OctetString, cASN1UTF8String; /* STRINGs */ +static VALUE cASN1NumericString, cASN1PrintableString; +static VALUE cASN1T61String, cASN1VideotexString; +static VALUE cASN1IA5String, cASN1GraphicString; +static VALUE cASN1ISO64String, cASN1GeneralString; +static VALUE cASN1UniversalString, cASN1BMPString; +static VALUE cASN1Null; /* NULL */ +static VALUE cASN1ObjectId; /* OBJECT IDENTIFIER */ +static VALUE cASN1UTCTime, cASN1GeneralizedTime; /* TIME */ +static VALUE cASN1Sequence, cASN1Set; /* CONSTRUCTIVE */ + +static VALUE sym_IMPLICIT, sym_EXPLICIT; +static VALUE sym_UNIVERSAL, sym_APPLICATION, sym_CONTEXT_SPECIFIC, sym_PRIVATE; +static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINDEFINITE_LENGTH, sivUNUSED_BITS; +static ID id_each; /* * DATE conversion */ +static VALUE +time_utc_new(VALUE args) +{ + return rb_funcallv(rb_cTime, rb_intern("utc"), 6, (VALUE *)args); +} + +static VALUE +time_utc_new_rescue(VALUE args, VALUE exc) +{ + rb_raise(eASN1Error, "invalid time"); +} + VALUE asn1time_to_time(const ASN1_TIME *time) { struct tm tm; - VALUE argv[6]; - int count; - - memset(&tm, 0, sizeof(struct tm)); - - switch (time->type) { - case V_ASN1_UTCTIME: - count = sscanf((const char *)time->data, "%2d%2d%2d%2d%2d%2dZ", - &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, - &tm.tm_sec); - - if (count == 5) { - tm.tm_sec = 0; - } else if (count != 6) { - ossl_raise(rb_eTypeError, "bad UTCTIME format: \"%s\"", - time->data); - } - if (tm.tm_year < 69) { - tm.tm_year += 2000; - } else { - tm.tm_year += 1900; - } - break; - case V_ASN1_GENERALIZEDTIME: - count = sscanf((const char *)time->data, "%4d%2d%2d%2d%2d%2dZ", - &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, - &tm.tm_sec); - if (count == 5) { - tm.tm_sec = 0; - } - else if (count != 6) { - ossl_raise(rb_eTypeError, "bad GENERALIZEDTIME format: \"%s\"", - time->data); - } - break; - default: - rb_warning("unknown time format"); - return Qnil; - } - argv[0] = INT2NUM(tm.tm_year); - argv[1] = INT2NUM(tm.tm_mon); - argv[2] = INT2NUM(tm.tm_mday); - argv[3] = INT2NUM(tm.tm_hour); - argv[4] = INT2NUM(tm.tm_min); - argv[5] = INT2NUM(tm.tm_sec); - - return rb_funcall2(rb_cTime, rb_intern("utc"), 6, argv); + if (!ASN1_TIME_to_tm(time, &tm)) + ossl_raise(eASN1Error, "ASN1_TIME_to_tm"); + + VALUE args[] = { + INT2NUM(tm.tm_year + 1900), + INT2NUM(tm.tm_mon + 1), + INT2NUM(tm.tm_mday), + INT2NUM(tm.tm_hour), + INT2NUM(tm.tm_min), + INT2NUM(tm.tm_sec), + }; + return rb_rescue2(time_utc_new, (VALUE)args, time_utc_new_rescue, Qnil, + rb_eArgError, 0); } static VALUE @@ -81,13 +98,13 @@ ossl_time_split(VALUE time, time_t *sec, int *days) VALUE num = rb_Integer(time); if (FIXNUM_P(num)) { - time_t t = FIX2LONG(num); - *sec = t % 86400; - *days = rb_long2int(t / 86400); + time_t t = FIX2LONG(num); + *sec = t % 86400; + *days = rb_long2int(t / 86400); } else { - *days = NUM2INT(rb_funcall(num, rb_intern("/"), 1, INT2FIX(86400))); - *sec = NUM2TIMET(rb_funcall(num, rb_intern("%"), 1, INT2FIX(86400))); + *days = NUM2INT(rb_funcall(num, rb_intern("/"), 1, INT2FIX(86400))); + *sec = NUM2TIMET(rb_funcall(num, rb_intern("%"), 1, INT2FIX(86400))); } } @@ -97,7 +114,8 @@ ossl_time_split(VALUE time, time_t *sec, int *days) VALUE asn1str_to_str(const ASN1_STRING *str) { - return rb_str_new((const char *)str->data, str->length); + return rb_str_new((const char *)ASN1_STRING_get0_data(str), + ASN1_STRING_length(str)); } /* @@ -110,16 +128,15 @@ asn1integer_to_num(const ASN1_INTEGER *ai) VALUE num; if (!ai) { - ossl_raise(rb_eTypeError, "ASN1_INTEGER is NULL!"); + ossl_raise(rb_eTypeError, "ASN1_INTEGER is NULL!"); } - if (ai->type == V_ASN1_ENUMERATED) - /* const_cast: workaround for old OpenSSL */ - bn = ASN1_ENUMERATED_to_BN((ASN1_ENUMERATED *)ai, NULL); + if (ASN1_STRING_type(ai) == V_ASN1_ENUMERATED) + bn = ASN1_ENUMERATED_to_BN(ai, NULL); else - bn = ASN1_INTEGER_to_BN(ai, NULL); + bn = ASN1_INTEGER_to_BN(ai, NULL); if (!bn) - ossl_raise(eOSSLError, NULL); + ossl_raise(eOSSLError, NULL); num = ossl_bn_new(bn); BN_free(bn); @@ -132,12 +149,12 @@ num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai) BIGNUM *bn; if (NIL_P(obj)) - ossl_raise(rb_eTypeError, "Can't convert nil into Integer"); + ossl_raise(rb_eTypeError, "Can't convert nil into Integer"); bn = GetBNPtr(obj); if (!(ai = BN_to_ASN1_INTEGER(bn, ai))) - ossl_raise(eOSSLError, NULL); + ossl_raise(eOSSLError, NULL); return ai; } @@ -148,48 +165,47 @@ asn1integer_to_num_i(VALUE arg) return asn1integer_to_num((ASN1_INTEGER *)arg); } -/********/ /* - * ASN1 module + * ASN1_OBJECT conversions */ -#define ossl_asn1_get_value(o) rb_attr_get((o),sivVALUE) -#define ossl_asn1_get_tag(o) rb_attr_get((o),sivTAG) -#define ossl_asn1_get_tagging(o) rb_attr_get((o),sivTAGGING) -#define ossl_asn1_get_tag_class(o) rb_attr_get((o),sivTAG_CLASS) -#define ossl_asn1_get_indefinite_length(o) rb_attr_get((o),sivINDEFINITE_LENGTH) - -#define ossl_asn1_set_value(o,v) rb_ivar_set((o),sivVALUE,(v)) -#define ossl_asn1_set_tag(o,v) rb_ivar_set((o),sivTAG,(v)) -#define ossl_asn1_set_tagging(o,v) rb_ivar_set((o),sivTAGGING,(v)) -#define ossl_asn1_set_tag_class(o,v) rb_ivar_set((o),sivTAG_CLASS,(v)) -#define ossl_asn1_set_indefinite_length(o,v) rb_ivar_set((o),sivINDEFINITE_LENGTH,(v)) +VALUE +ossl_asn1obj_to_string_oid(const ASN1_OBJECT *a1obj) +{ + VALUE str; + int len; -VALUE mASN1; -VALUE eASN1Error; + str = rb_usascii_str_new(NULL, 127); + len = OBJ_obj2txt(RSTRING_PTR(str), RSTRING_LENINT(str), a1obj, 1); + if (len <= 0 || len == INT_MAX) + ossl_raise(eOSSLError, "OBJ_obj2txt"); + if (len > RSTRING_LEN(str)) { + /* +1 is for the \0 terminator added by OBJ_obj2txt() */ + rb_str_resize(str, len + 1); + len = OBJ_obj2txt(RSTRING_PTR(str), len + 1, a1obj, 1); + if (len <= 0) + ossl_raise(eOSSLError, "OBJ_obj2txt"); + } + rb_str_set_len(str, len); + return str; +} -VALUE cASN1Data; -VALUE cASN1Primitive; -VALUE cASN1Constructive; - -VALUE cASN1EndOfContent; -VALUE cASN1Boolean; /* BOOLEAN */ -VALUE cASN1Integer, cASN1Enumerated; /* INTEGER */ -VALUE cASN1BitString; /* BIT STRING */ -VALUE cASN1OctetString, cASN1UTF8String; /* STRINGs */ -VALUE cASN1NumericString, cASN1PrintableString; -VALUE cASN1T61String, cASN1VideotexString; -VALUE cASN1IA5String, cASN1GraphicString; -VALUE cASN1ISO64String, cASN1GeneralString; -VALUE cASN1UniversalString, cASN1BMPString; -VALUE cASN1Null; /* NULL */ -VALUE cASN1ObjectId; /* OBJECT IDENTIFIER */ -VALUE cASN1UTCTime, cASN1GeneralizedTime; /* TIME */ -VALUE cASN1Sequence, cASN1Set; /* CONSTRUCTIVE */ +VALUE +ossl_asn1obj_to_string(const ASN1_OBJECT *obj) +{ + int nid = OBJ_obj2nid(obj); + if (nid != NID_undef) + return rb_str_new_cstr(OBJ_nid2sn(nid)); + return ossl_asn1obj_to_string_oid(obj); +} -static VALUE sym_IMPLICIT, sym_EXPLICIT; -static VALUE sym_UNIVERSAL, sym_APPLICATION, sym_CONTEXT_SPECIFIC, sym_PRIVATE; -static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINDEFINITE_LENGTH, sivUNUSED_BITS; -static ID id_each; +VALUE +ossl_asn1obj_to_string_long_name(const ASN1_OBJECT *obj) +{ + int nid = OBJ_obj2nid(obj); + if (nid != NID_undef) + return rb_str_new_cstr(OBJ_nid2ln(nid)); + return ossl_asn1obj_to_string_oid(obj); +} /* * Ruby to ASN1 converters @@ -198,9 +214,9 @@ static ASN1_BOOLEAN obj_to_asn1bool(VALUE obj) { if (NIL_P(obj)) - ossl_raise(rb_eTypeError, "Can't convert nil into Boolean"); + ossl_raise(rb_eTypeError, "Can't convert nil into Boolean"); - return RTEST(obj) ? 0xff : 0x0; + return RTEST(obj) ? 0xff : 0x0; } static ASN1_INTEGER* @@ -215,11 +231,11 @@ obj_to_asn1bstr(VALUE obj, long unused_bits) ASN1_BIT_STRING *bstr; if (unused_bits < 0 || unused_bits > 7) - ossl_raise(eASN1Error, "unused_bits for a bitstring value must be in "\ - "the range 0 to 7"); + ossl_raise(eASN1Error, "unused_bits for a bitstring value must be in "\ + "the range 0 to 7"); StringValue(obj); if(!(bstr = ASN1_BIT_STRING_new())) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); ASN1_BIT_STRING_set(bstr, (unsigned char *)RSTRING_PTR(obj), RSTRING_LENINT(obj)); bstr->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07); /* clear */ bstr->flags |= ASN1_STRING_FLAG_BITS_LEFT | unused_bits; @@ -234,7 +250,7 @@ obj_to_asn1str(VALUE obj) StringValue(obj); if(!(str = ASN1_STRING_new())) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); ASN1_STRING_set(str, RSTRING_PTR(obj), RSTRING_LENINT(obj)); return str; @@ -246,15 +262,15 @@ obj_to_asn1null(VALUE obj) ASN1_NULL *null; if(!NIL_P(obj)) - ossl_raise(eASN1Error, "nil expected"); + ossl_raise(eASN1Error, "nil expected"); if(!(null = ASN1_NULL_new())) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); return null; } -static ASN1_OBJECT* -obj_to_asn1obj(VALUE obj) +ASN1_OBJECT * +ossl_to_asn1obj(VALUE obj) { ASN1_OBJECT *a1obj; @@ -276,7 +292,7 @@ obj_to_asn1utime(VALUE time) ossl_time_split(time, &sec, &off_days); if (!(t = ASN1_UTCTIME_adj(NULL, sec, off_days, 0))) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); return t; } @@ -291,7 +307,7 @@ obj_to_asn1gtime(VALUE time) ossl_time_split(time, &sec, &off_days); if (!(t = ASN1_GENERALIZEDTIME_adj(NULL, sec, off_days, 0))) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); return t; } @@ -304,7 +320,7 @@ obj_to_asn1derstr(VALUE obj) str = ossl_to_der(obj); if(!(a1str = ASN1_STRING_new())) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); ASN1_STRING_set(a1str, RSTRING_PTR(str), RSTRING_LENINT(str)); return a1str; @@ -319,9 +335,9 @@ decode_bool(unsigned char* der, long length) const unsigned char *p = der; if (length != 3) - ossl_raise(eASN1Error, "invalid length for BOOLEAN"); + ossl_raise(eASN1Error, "invalid length for BOOLEAN"); if (p[0] != 1 || p[1] != 1) - ossl_raise(eASN1Error, "invalid BOOLEAN"); + ossl_raise(eASN1Error, "invalid BOOLEAN"); return p[2] ? Qtrue : Qfalse; } @@ -336,9 +352,9 @@ decode_int(unsigned char* der, long length) p = der; if(!(ai = d2i_ASN1_INTEGER(NULL, &p, length))) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); ret = rb_protect(asn1integer_to_num_i, - (VALUE)ai, &status); + (VALUE)ai, &status); ASN1_INTEGER_free(ai); if(status) rb_jump_tag(status); @@ -355,11 +371,11 @@ decode_bstr(unsigned char* der, long length, long *unused_bits) p = der; if(!(bstr = d2i_ASN1_BIT_STRING(NULL, &p, length))) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); len = bstr->length; *unused_bits = 0; if(bstr->flags & ASN1_STRING_FLAG_BITS_LEFT) - *unused_bits = bstr->flags & 0x07; + *unused_bits = bstr->flags & 0x07; ret = rb_str_new((const char *)bstr->data, len); ASN1_BIT_STRING_free(bstr); @@ -376,9 +392,9 @@ decode_enum(unsigned char* der, long length) p = der; if(!(ai = d2i_ASN1_ENUMERATED(NULL, &p, length))) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); ret = rb_protect(asn1integer_to_num_i, - (VALUE)ai, &status); + (VALUE)ai, &status); ASN1_ENUMERATED_free(ai); if(status) rb_jump_tag(status); @@ -393,38 +409,33 @@ decode_null(unsigned char* der, long length) p = der; if(!(null = d2i_ASN1_NULL(NULL, &p, length))) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); ASN1_NULL_free(null); return Qnil; } +VALUE +asn1obj_to_string_i(VALUE arg) +{ + return ossl_asn1obj_to_string((const ASN1_OBJECT *)arg); +} + static VALUE decode_obj(unsigned char* der, long length) { ASN1_OBJECT *obj; const unsigned char *p; VALUE ret; - int nid; - BIO *bio; + int state; p = der; - if(!(obj = d2i_ASN1_OBJECT(NULL, &p, length))) - ossl_raise(eASN1Error, NULL); - if((nid = OBJ_obj2nid(obj)) != NID_undef){ - ASN1_OBJECT_free(obj); - ret = rb_str_new2(OBJ_nid2sn(nid)); - } - else{ - if(!(bio = BIO_new(BIO_s_mem()))){ - ASN1_OBJECT_free(obj); - ossl_raise(eASN1Error, NULL); - } - i2a_ASN1_OBJECT(bio, obj); - ASN1_OBJECT_free(obj); - ret = ossl_membio2str(bio); - } - + if (!(obj = d2i_ASN1_OBJECT(NULL, &p, length))) + ossl_raise(eASN1Error, "d2i_ASN1_OBJECT"); + ret = rb_protect(asn1obj_to_string_i, (VALUE)obj, &state); + ASN1_OBJECT_free(obj); + if (state) + rb_jump_tag(state); return ret; } @@ -438,9 +449,9 @@ decode_time(unsigned char* der, long length) p = der; if(!(time = d2i_ASN1_TIME(NULL, &p, length))) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); ret = rb_protect(asn1time_to_time_i, - (VALUE)time, &status); + (VALUE)time, &status); ASN1_TIME_free(time); if(status) rb_jump_tag(status); @@ -451,7 +462,7 @@ static VALUE decode_eoc(unsigned char *der, long length) { if (length != 2 || !(der[0] == 0x00 && der[1] == 0x00)) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); return rb_str_new("", 0); } @@ -503,7 +514,7 @@ static VALUE class_tag_map; static int ossl_asn1_default_tag(VALUE obj); -ASN1_TYPE* +static ASN1_TYPE * ossl_asn1_get_asn1type(VALUE obj) { ASN1_TYPE *ret; @@ -516,62 +527,62 @@ ossl_asn1_get_asn1type(VALUE obj) tag = ossl_asn1_default_tag(obj); value = ossl_asn1_get_value(obj); switch(tag){ - case V_ASN1_BOOLEAN: - ptr = (void*)(VALUE)obj_to_asn1bool(value); - free_func = NULL; - break; - case V_ASN1_INTEGER: /* FALLTHROUGH */ - case V_ASN1_ENUMERATED: - ptr = obj_to_asn1int(value); - free_func = (free_func_type *)ASN1_INTEGER_free; - break; - case V_ASN1_BIT_STRING: + case V_ASN1_BOOLEAN: + ptr = (void*)(VALUE)obj_to_asn1bool(value); + free_func = NULL; + break; + case V_ASN1_INTEGER: /* FALLTHROUGH */ + case V_ASN1_ENUMERATED: + ptr = obj_to_asn1int(value); + free_func = (free_func_type *)ASN1_INTEGER_free; + break; + case V_ASN1_BIT_STRING: rflag = rb_attr_get(obj, sivUNUSED_BITS); - ptr = obj_to_asn1bstr(value, NUM2INT(rflag)); - free_func = (free_func_type *)ASN1_BIT_STRING_free; - break; - case V_ASN1_NULL: - ptr = obj_to_asn1null(value); - free_func = (free_func_type *)ASN1_NULL_free; - break; - case V_ASN1_OCTET_STRING: /* FALLTHROUGH */ - case V_ASN1_UTF8STRING: /* FALLTHROUGH */ - case V_ASN1_NUMERICSTRING: /* FALLTHROUGH */ - case V_ASN1_PRINTABLESTRING: /* FALLTHROUGH */ - case V_ASN1_T61STRING: /* FALLTHROUGH */ - case V_ASN1_VIDEOTEXSTRING: /* FALLTHROUGH */ - case V_ASN1_IA5STRING: /* FALLTHROUGH */ - case V_ASN1_GRAPHICSTRING: /* FALLTHROUGH */ - case V_ASN1_ISO64STRING: /* FALLTHROUGH */ - case V_ASN1_GENERALSTRING: /* FALLTHROUGH */ - case V_ASN1_UNIVERSALSTRING: /* FALLTHROUGH */ - case V_ASN1_BMPSTRING: - ptr = obj_to_asn1str(value); - free_func = (free_func_type *)ASN1_STRING_free; - break; - case V_ASN1_OBJECT: - ptr = obj_to_asn1obj(value); - free_func = (free_func_type *)ASN1_OBJECT_free; - break; - case V_ASN1_UTCTIME: - ptr = obj_to_asn1utime(value); - free_func = (free_func_type *)ASN1_TIME_free; - break; - case V_ASN1_GENERALIZEDTIME: - ptr = obj_to_asn1gtime(value); - free_func = (free_func_type *)ASN1_TIME_free; - break; - case V_ASN1_SET: /* FALLTHROUGH */ - case V_ASN1_SEQUENCE: - ptr = obj_to_asn1derstr(obj); - free_func = (free_func_type *)ASN1_STRING_free; - break; - default: - ossl_raise(eASN1Error, "unsupported ASN.1 type"); + ptr = obj_to_asn1bstr(value, NUM2INT(rflag)); + free_func = (free_func_type *)ASN1_BIT_STRING_free; + break; + case V_ASN1_NULL: + ptr = obj_to_asn1null(value); + free_func = (free_func_type *)ASN1_NULL_free; + break; + case V_ASN1_OCTET_STRING: /* FALLTHROUGH */ + case V_ASN1_UTF8STRING: /* FALLTHROUGH */ + case V_ASN1_NUMERICSTRING: /* FALLTHROUGH */ + case V_ASN1_PRINTABLESTRING: /* FALLTHROUGH */ + case V_ASN1_T61STRING: /* FALLTHROUGH */ + case V_ASN1_VIDEOTEXSTRING: /* FALLTHROUGH */ + case V_ASN1_IA5STRING: /* FALLTHROUGH */ + case V_ASN1_GRAPHICSTRING: /* FALLTHROUGH */ + case V_ASN1_ISO64STRING: /* FALLTHROUGH */ + case V_ASN1_GENERALSTRING: /* FALLTHROUGH */ + case V_ASN1_UNIVERSALSTRING: /* FALLTHROUGH */ + case V_ASN1_BMPSTRING: + ptr = obj_to_asn1str(value); + free_func = (free_func_type *)ASN1_STRING_free; + break; + case V_ASN1_OBJECT: + ptr = ossl_to_asn1obj(value); + free_func = (free_func_type *)ASN1_OBJECT_free; + break; + case V_ASN1_UTCTIME: + ptr = obj_to_asn1utime(value); + free_func = (free_func_type *)ASN1_TIME_free; + break; + case V_ASN1_GENERALIZEDTIME: + ptr = obj_to_asn1gtime(value); + free_func = (free_func_type *)ASN1_TIME_free; + break; + case V_ASN1_SET: /* FALLTHROUGH */ + case V_ASN1_SEQUENCE: + ptr = obj_to_asn1derstr(obj); + free_func = (free_func_type *)ASN1_STRING_free; + break; + default: + ossl_raise(eASN1Error, "unsupported ASN.1 type"); } if(!(ret = OPENSSL_malloc(sizeof(ASN1_TYPE)))){ - if(free_func) free_func(ptr); - ossl_raise(eASN1Error, "ASN1_TYPE alloc failure"); + if(free_func) free_func(ptr); + ossl_raise(eASN1Error, "ASN1_TYPE alloc failure"); } memset(ret, 0, sizeof(ASN1_TYPE)); ASN1_TYPE_set(ret, tag, ptr); @@ -586,10 +597,10 @@ ossl_asn1_default_tag(VALUE obj) tmp_class = CLASS_OF(obj); while (!NIL_P(tmp_class)) { - tag = rb_hash_lookup(class_tag_map, tmp_class); - if (tag != Qnil) - return NUM2INT(tag); - tmp_class = rb_class_superclass(tmp_class); + tag = rb_hash_lookup(class_tag_map, tmp_class); + if (tag != Qnil) + return NUM2INT(tag); + tmp_class = rb_class_superclass(tmp_class); } return -1; @@ -602,7 +613,7 @@ ossl_asn1_tag(VALUE obj) tag = ossl_asn1_get_tag(obj); if(NIL_P(tag)) - ossl_raise(eASN1Error, "tag number not specified"); + ossl_raise(eASN1Error, "tag number not specified"); return NUM2INT(tag); } @@ -614,28 +625,28 @@ ossl_asn1_tag_class(VALUE obj) s = ossl_asn1_get_tag_class(obj); if (NIL_P(s) || s == sym_UNIVERSAL) - return V_ASN1_UNIVERSAL; + return V_ASN1_UNIVERSAL; else if (s == sym_APPLICATION) - return V_ASN1_APPLICATION; + return V_ASN1_APPLICATION; else if (s == sym_CONTEXT_SPECIFIC) - return V_ASN1_CONTEXT_SPECIFIC; + return V_ASN1_CONTEXT_SPECIFIC; else if (s == sym_PRIVATE) - return V_ASN1_PRIVATE; + return V_ASN1_PRIVATE; else - ossl_raise(eASN1Error, "invalid tag class"); + ossl_raise(eASN1Error, "invalid tag class"); } static VALUE ossl_asn1_class2sym(int tc) { if((tc & V_ASN1_PRIVATE) == V_ASN1_PRIVATE) - return sym_PRIVATE; + return sym_PRIVATE; else if((tc & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC) - return sym_CONTEXT_SPECIFIC; + return sym_CONTEXT_SPECIFIC; else if((tc & V_ASN1_APPLICATION) == V_ASN1_APPLICATION) - return sym_APPLICATION; + return sym_APPLICATION; else - return sym_UNIVERSAL; + return sym_UNIVERSAL; } /* @@ -658,7 +669,7 @@ static VALUE ossl_asn1data_initialize(VALUE self, VALUE value, VALUE tag, VALUE tag_class) { if(!SYMBOL_P(tag_class)) - ossl_raise(eASN1Error, "invalid tag class"); + ossl_raise(eASN1Error, "invalid tag class"); ossl_asn1_set_tag(self, tag); ossl_asn1_set_value(self, value); ossl_asn1_set_tag_class(self, tag_class); @@ -680,35 +691,35 @@ to_der_internal(VALUE self, int constructed, int indef_len, VALUE body) body_length = RSTRING_LENINT(body); if (ossl_asn1_get_tagging(self) == sym_EXPLICIT) { - int inner_length, e_encoding = indef_len ? 2 : 1; - - if (default_tag_number == -1) - ossl_raise(eASN1Error, "explicit tagging of unknown tag"); - - inner_length = ASN1_object_size(encoding, body_length, default_tag_number); - total_length = ASN1_object_size(e_encoding, inner_length, tag_number); - str = rb_str_new(NULL, total_length); - p = (unsigned char *)RSTRING_PTR(str); - /* Put explicit tag */ - ASN1_put_object(&p, e_encoding, inner_length, tag_number, tag_class); - /* Append inner object */ - ASN1_put_object(&p, encoding, body_length, default_tag_number, V_ASN1_UNIVERSAL); - memcpy(p, RSTRING_PTR(body), body_length); - p += body_length; - if (indef_len) { - ASN1_put_eoc(&p); /* For inner object */ - ASN1_put_eoc(&p); /* For wrapper object */ - } + int inner_length, e_encoding = indef_len ? 2 : 1; + + if (default_tag_number == -1) + ossl_raise(eASN1Error, "explicit tagging of unknown tag"); + + inner_length = ASN1_object_size(encoding, body_length, default_tag_number); + total_length = ASN1_object_size(e_encoding, inner_length, tag_number); + str = rb_str_new(NULL, total_length); + p = (unsigned char *)RSTRING_PTR(str); + /* Put explicit tag */ + ASN1_put_object(&p, e_encoding, inner_length, tag_number, tag_class); + /* Append inner object */ + ASN1_put_object(&p, encoding, body_length, default_tag_number, V_ASN1_UNIVERSAL); + memcpy(p, RSTRING_PTR(body), body_length); + p += body_length; + if (indef_len) { + ASN1_put_eoc(&p); /* For inner object */ + ASN1_put_eoc(&p); /* For wrapper object */ + } } else { - total_length = ASN1_object_size(encoding, body_length, tag_number); - str = rb_str_new(NULL, total_length); - p = (unsigned char *)RSTRING_PTR(str); - ASN1_put_object(&p, encoding, body_length, tag_number, tag_class); - memcpy(p, RSTRING_PTR(body), body_length); - p += body_length; - if (indef_len) - ASN1_put_eoc(&p); + total_length = ASN1_object_size(encoding, body_length, tag_number); + str = rb_str_new(NULL, total_length); + p = (unsigned char *)RSTRING_PTR(str); + ASN1_put_object(&p, encoding, body_length, tag_number, tag_class); + memcpy(p, RSTRING_PTR(body), body_length); + p += body_length; + if (indef_len) + ASN1_put_eoc(&p); } assert(p - (unsigned char *)RSTRING_PTR(str) == total_length); return str; @@ -731,18 +742,22 @@ ossl_asn1data_to_der(VALUE self) VALUE value = ossl_asn1_get_value(self); if (rb_obj_is_kind_of(value, rb_cArray)) - return ossl_asn1cons_to_der(self); + return ossl_asn1cons_to_der(self); else { - if (RTEST(ossl_asn1_get_indefinite_length(self))) - ossl_raise(eASN1Error, "indefinite length form cannot be used " \ - "with primitive encoding"); - return ossl_asn1prim_to_der(self); + if (RTEST(ossl_asn1_get_indefinite_length(self))) + ossl_raise(eASN1Error, "indefinite length form cannot be used " \ + "with primitive encoding"); + return ossl_asn1prim_to_der(self); } } +static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self); +static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset, + int depth, int yield, long *num_read); + static VALUE int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag, - VALUE tc, long *num_read) + VALUE tc, long *num_read) { VALUE value, asn1data; unsigned char *p; @@ -751,64 +766,64 @@ int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag, p = *pp; if(tc == sym_UNIVERSAL && tag < ossl_asn1_info_size) { - switch(tag){ - case V_ASN1_EOC: - value = decode_eoc(p, hlen+length); - break; - case V_ASN1_BOOLEAN: - value = decode_bool(p, hlen+length); - break; - case V_ASN1_INTEGER: - value = decode_int(p, hlen+length); - break; - case V_ASN1_BIT_STRING: - value = decode_bstr(p, hlen+length, &flag); - break; - case V_ASN1_NULL: - value = decode_null(p, hlen+length); - break; - case V_ASN1_ENUMERATED: - value = decode_enum(p, hlen+length); - break; - case V_ASN1_OBJECT: - value = decode_obj(p, hlen+length); - break; - case V_ASN1_UTCTIME: /* FALLTHROUGH */ - case V_ASN1_GENERALIZEDTIME: - value = decode_time(p, hlen+length); - break; - default: - /* use original value */ - p += hlen; - value = rb_str_new((const char *)p, length); - break; - } + switch(tag){ + case V_ASN1_EOC: + value = decode_eoc(p, hlen+length); + break; + case V_ASN1_BOOLEAN: + value = decode_bool(p, hlen+length); + break; + case V_ASN1_INTEGER: + value = decode_int(p, hlen+length); + break; + case V_ASN1_BIT_STRING: + value = decode_bstr(p, hlen+length, &flag); + break; + case V_ASN1_NULL: + value = decode_null(p, hlen+length); + break; + case V_ASN1_ENUMERATED: + value = decode_enum(p, hlen+length); + break; + case V_ASN1_OBJECT: + value = decode_obj(p, hlen+length); + break; + case V_ASN1_UTCTIME: /* FALLTHROUGH */ + case V_ASN1_GENERALIZEDTIME: + value = decode_time(p, hlen+length); + break; + default: + /* use original value */ + p += hlen; + value = rb_str_new((const char *)p, length); + break; + } } else { - p += hlen; - value = rb_str_new((const char *)p, length); + p += hlen; + value = rb_str_new((const char *)p, length); } *pp += hlen + length; *num_read = hlen + length; if (tc == sym_UNIVERSAL && - tag < ossl_asn1_info_size && ossl_asn1_info[tag].klass) { - VALUE klass = *ossl_asn1_info[tag].klass; - VALUE args[4]; - args[0] = value; - args[1] = INT2NUM(tag); - args[2] = Qnil; - args[3] = tc; - asn1data = rb_obj_alloc(klass); - ossl_asn1_initialize(4, args, asn1data); - if(tag == V_ASN1_BIT_STRING){ - rb_ivar_set(asn1data, sivUNUSED_BITS, LONG2NUM(flag)); - } + tag < ossl_asn1_info_size && ossl_asn1_info[tag].klass) { + VALUE klass = *ossl_asn1_info[tag].klass; + VALUE args[4]; + args[0] = value; + args[1] = INT2NUM(tag); + args[2] = Qnil; + args[3] = tc; + asn1data = rb_obj_alloc(klass); + ossl_asn1_initialize(4, args, asn1data); + if(tag == V_ASN1_BIT_STRING){ + rb_ivar_set(asn1data, sivUNUSED_BITS, LONG2NUM(flag)); + } } else { - asn1data = rb_obj_alloc(cASN1Data); - ossl_asn1data_initialize(asn1data, value, INT2NUM(tag), tc); + asn1data = rb_obj_alloc(cASN1Data); + ossl_asn1data_initialize(asn1data, value, INT2NUM(tag), tc); } return asn1data; @@ -816,8 +831,8 @@ int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag, static VALUE int_ossl_asn1_decode0_cons(unsigned char **pp, long max_len, long length, - long *offset, int depth, int yield, int j, - int tag, VALUE tc, long *num_read) + long *offset, int depth, int yield, int j, + int tag, VALUE tc, long *num_read) { VALUE value, asn1data, ary; int indefinite; @@ -828,40 +843,42 @@ int_ossl_asn1_decode0_cons(unsigned char **pp, long max_len, long length, available_len = indefinite ? max_len : length; while (available_len > 0) { - long inner_read = 0; - value = ossl_asn1_decode0(pp, available_len, &off, depth + 1, yield, &inner_read); - *num_read += inner_read; - available_len -= inner_read; - - if (indefinite && - ossl_asn1_tag(value) == V_ASN1_EOC && - ossl_asn1_get_tag_class(value) == sym_UNIVERSAL) { - break; - } - rb_ary_push(ary, value); + long inner_read = 0; + value = ossl_asn1_decode0(pp, available_len, &off, depth + 1, yield, &inner_read); + *num_read += inner_read; + available_len -= inner_read; + + if (indefinite) { + if (ossl_asn1_tag(value) == V_ASN1_EOC && + ossl_asn1_get_tag_class(value) == sym_UNIVERSAL) + break; + if (available_len == 0) + ossl_raise(eASN1Error, "EOC missing in indefinite length encoding"); + } + rb_ary_push(ary, value); } if (tc == sym_UNIVERSAL) { - VALUE args[4]; - if (tag == V_ASN1_SEQUENCE || tag == V_ASN1_SET) - asn1data = rb_obj_alloc(*ossl_asn1_info[tag].klass); - else - asn1data = rb_obj_alloc(cASN1Constructive); - args[0] = ary; - args[1] = INT2NUM(tag); - args[2] = Qnil; - args[3] = tc; - ossl_asn1_initialize(4, args, asn1data); + VALUE args[4]; + if (tag == V_ASN1_SEQUENCE || tag == V_ASN1_SET) + asn1data = rb_obj_alloc(*ossl_asn1_info[tag].klass); + else + asn1data = rb_obj_alloc(cASN1Constructive); + args[0] = ary; + args[1] = INT2NUM(tag); + args[2] = Qnil; + args[3] = tc; + ossl_asn1_initialize(4, args, asn1data); } else { - asn1data = rb_obj_alloc(cASN1Data); - ossl_asn1data_initialize(asn1data, ary, INT2NUM(tag), tc); + asn1data = rb_obj_alloc(cASN1Data); + ossl_asn1data_initialize(asn1data, ary, INT2NUM(tag), tc); } if (indefinite) - ossl_asn1_set_indefinite_length(asn1data, Qtrue); + ossl_asn1_set_indefinite_length(asn1data, Qtrue); else - ossl_asn1_set_indefinite_length(asn1data, Qfalse); + ossl_asn1_set_indefinite_length(asn1data, Qfalse); *offset = off; return asn1data; @@ -869,7 +886,7 @@ int_ossl_asn1_decode0_cons(unsigned char **pp, long max_len, long length, static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset, int depth, - int yield, long *num_read) + int yield, long *num_read) { unsigned char *start, *p; const unsigned char *p0; @@ -885,46 +902,46 @@ ossl_asn1_decode0(unsigned char **pp, long length, long *offset, int depth, if(j & 0x80) ossl_raise(eASN1Error, NULL); if(len > length) ossl_raise(eASN1Error, "value is too short"); if((tc & V_ASN1_PRIVATE) == V_ASN1_PRIVATE) - tag_class = sym_PRIVATE; + tag_class = sym_PRIVATE; else if((tc & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC) - tag_class = sym_CONTEXT_SPECIFIC; + tag_class = sym_CONTEXT_SPECIFIC; else if((tc & V_ASN1_APPLICATION) == V_ASN1_APPLICATION) - tag_class = sym_APPLICATION; + tag_class = sym_APPLICATION; else - tag_class = sym_UNIVERSAL; + tag_class = sym_UNIVERSAL; hlen = p - start; if(yield) { - VALUE arg = rb_ary_new(); - rb_ary_push(arg, LONG2NUM(depth)); - rb_ary_push(arg, LONG2NUM(*offset)); - rb_ary_push(arg, LONG2NUM(hlen)); - rb_ary_push(arg, LONG2NUM(len)); - rb_ary_push(arg, (j & V_ASN1_CONSTRUCTED) ? Qtrue : Qfalse); - rb_ary_push(arg, ossl_asn1_class2sym(tc)); - rb_ary_push(arg, INT2NUM(tag)); - rb_yield(arg); + VALUE arg = rb_ary_new(); + rb_ary_push(arg, LONG2NUM(depth)); + rb_ary_push(arg, LONG2NUM(*offset)); + rb_ary_push(arg, LONG2NUM(hlen)); + rb_ary_push(arg, LONG2NUM(len)); + rb_ary_push(arg, (j & V_ASN1_CONSTRUCTED) ? Qtrue : Qfalse); + rb_ary_push(arg, ossl_asn1_class2sym(tc)); + rb_ary_push(arg, INT2NUM(tag)); + rb_yield(arg); } if(j & V_ASN1_CONSTRUCTED) { - *pp += hlen; - off += hlen; - asn1data = int_ossl_asn1_decode0_cons(pp, length - hlen, len, &off, depth, yield, j, tag, tag_class, &inner_read); - inner_read += hlen; + *pp += hlen; + off += hlen; + asn1data = int_ossl_asn1_decode0_cons(pp, length - hlen, len, &off, depth, yield, j, tag, tag_class, &inner_read); + inner_read += hlen; } else { - if ((j & 0x01) && (len == 0)) - ossl_raise(eASN1Error, "indefinite length for primitive value"); - asn1data = int_ossl_asn1_decode0_prim(pp, len, hlen, tag, tag_class, &inner_read); - off += hlen + len; + if ((j & 0x01) && (len == 0)) + ossl_raise(eASN1Error, "indefinite length for primitive value"); + asn1data = int_ossl_asn1_decode0_prim(pp, len, hlen, tag, tag_class, &inner_read); + off += hlen + len; } if (num_read) - *num_read = inner_read; + *num_read = inner_read; if (len != 0 && inner_read != hlen + len) { - ossl_raise(eASN1Error, - "Type mismatch. Bytes read: %ld Bytes available: %ld", - inner_read, hlen + len); + ossl_raise(eASN1Error, + "Type mismatch. Bytes read: %ld Bytes available: %ld", + inner_read, hlen + len); } *offset = off; @@ -935,9 +952,9 @@ static void int_ossl_decode_sanity_check(long len, long read, long offset) { if (len != 0 && (read != len || offset != len)) { - ossl_raise(eASN1Error, - "Type mismatch. Total bytes read: %ld Bytes available: %ld Offset: %ld", - read, len, offset); + ossl_raise(eASN1Error, + "Type mismatch. Total bytes read: %ld Bytes available: %ld Offset: %ld", + read, len, offset); } } @@ -1037,11 +1054,11 @@ ossl_asn1_decode_all(VALUE self, VALUE obj) tmp_len = len; ary = rb_ary_new(); while (tmp_len > 0) { - long tmp_read = 0; - val = ossl_asn1_decode0(&p, tmp_len, &offset, 0, 0, &tmp_read); - rb_ary_push(ary, val); - read += tmp_read; - tmp_len -= tmp_read; + long tmp_read = 0; + val = ossl_asn1_decode0(&p, tmp_len, &offset, 0, 0, &tmp_read); + rb_ary_push(ary, val); + read += tmp_read; + tmp_len -= tmp_read; } RB_GC_GUARD(tmp); int_ossl_decode_sanity_check(len, read, offset); @@ -1081,23 +1098,23 @@ ossl_asn1_initialize(int argc, VALUE *argv, VALUE self) default_tag = ossl_asn1_default_tag(self); if (default_tag == -1 || argc > 1) { - if(NIL_P(tag)) - ossl_raise(eASN1Error, "must specify tag number"); - if(!NIL_P(tagging) && !SYMBOL_P(tagging)) - ossl_raise(eASN1Error, "invalid tagging method"); - if(NIL_P(tag_class)) { - if (NIL_P(tagging)) - tag_class = sym_UNIVERSAL; - else - tag_class = sym_CONTEXT_SPECIFIC; - } - if(!SYMBOL_P(tag_class)) - ossl_raise(eASN1Error, "invalid tag class"); + if(NIL_P(tag)) + ossl_raise(eASN1Error, "must specify tag number"); + if(!NIL_P(tagging) && !SYMBOL_P(tagging)) + ossl_raise(eASN1Error, "invalid tagging method"); + if(NIL_P(tag_class)) { + if (NIL_P(tagging)) + tag_class = sym_UNIVERSAL; + else + tag_class = sym_CONTEXT_SPECIFIC; + } + if(!SYMBOL_P(tag_class)) + ossl_raise(eASN1Error, "invalid tag class"); } else{ - tag = INT2NUM(default_tag); - tagging = Qnil; - tag_class = sym_UNIVERSAL; + tag = INT2NUM(default_tag); + tagging = Qnil; + tag_class = sym_UNIVERSAL; } ossl_asn1_set_tag(self, tag); ossl_asn1_set_value(self, value); @@ -1105,7 +1122,7 @@ ossl_asn1_initialize(int argc, VALUE *argv, VALUE self) ossl_asn1_set_tag_class(self, tag_class); ossl_asn1_set_indefinite_length(self, Qfalse); if (default_tag == V_ASN1_BIT_STRING) - rb_ivar_set(self, sivUNUSED_BITS, INT2FIX(0)); + rb_ivar_set(self, sivUNUSED_BITS, INT2FIX(0)); return self; } @@ -1147,30 +1164,33 @@ ossl_asn1prim_to_der(VALUE self) VALUE str; if (ossl_asn1_default_tag(self) == -1) { - str = ossl_asn1_get_value(self); - return to_der_internal(self, 0, 0, StringValue(str)); + str = ossl_asn1_get_value(self); + return to_der_internal(self, 0, 0, StringValue(str)); } asn1 = ossl_asn1_get_asn1type(self); alllen = i2d_ASN1_TYPE(asn1, NULL); if (alllen < 0) { - ASN1_TYPE_free(asn1); - ossl_raise(eASN1Error, "i2d_ASN1_TYPE"); + ASN1_TYPE_free(asn1); + ossl_raise(eASN1Error, "i2d_ASN1_TYPE"); } str = ossl_str_new(NULL, alllen, &state); if (state) { - ASN1_TYPE_free(asn1); - rb_jump_tag(state); + ASN1_TYPE_free(asn1); + rb_jump_tag(state); } p0 = p1 = (unsigned char *)RSTRING_PTR(str); - i2d_ASN1_TYPE(asn1, &p0); + if (i2d_ASN1_TYPE(asn1, &p0) < 0) { + ASN1_TYPE_free(asn1); + ossl_raise(eASN1Error, "i2d_ASN1_TYPE"); + } ASN1_TYPE_free(asn1); - assert(p0 - p1 == alllen); + ossl_str_adjust(str, p0); /* Strip header since to_der_internal() wants only the payload */ j = ASN1_get_object((const unsigned char **)&p1, &bodylen, &tag, &tc, alllen); if (j & 0x80) - ossl_raise(eASN1Error, "ASN1_get_object"); /* should not happen */ + ossl_raise(eASN1Error, "ASN1_get_object"); /* should not happen */ return to_der_internal(self, 0, 0, rb_str_drop_bytes(str, alllen - bodylen)); } @@ -1192,22 +1212,22 @@ ossl_asn1cons_to_der(VALUE self) ary = rb_convert_type(ossl_asn1_get_value(self), T_ARRAY, "Array", "to_a"); str = rb_str_new(NULL, 0); for (i = 0; i < RARRAY_LEN(ary); i++) { - VALUE item = RARRAY_AREF(ary, i); - - if (indef_len && rb_obj_is_kind_of(item, cASN1EndOfContent)) { - if (i != RARRAY_LEN(ary) - 1) - ossl_raise(eASN1Error, "illegal EOC octets in value"); - - /* - * EOC is not really part of the content, but we required to add one - * at the end in the past. - */ - break; - } - - item = ossl_to_der_if_possible(item); - StringValue(item); - rb_str_append(str, item); + VALUE item = RARRAY_AREF(ary, i); + + if (indef_len && rb_obj_is_kind_of(item, cASN1EndOfContent)) { + if (i != RARRAY_LEN(ary) - 1) + ossl_raise(eASN1Error, "illegal EOC octets in value"); + + /* + * EOC is not really part of the content, but we required to add one + * at the end in the past. + */ + break; + } + + item = ossl_to_der_if_possible(item); + StringValue(item); + rb_str_append(str, item); } return to_der_internal(self, 1, indef_len, str); @@ -1253,7 +1273,7 @@ ossl_asn1obj_s_register(VALUE self, VALUE oid, VALUE sn, VALUE ln) StringValueCStr(ln); if(!OBJ_create(RSTRING_PTR(oid), RSTRING_PTR(sn), RSTRING_PTR(ln))) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); return Qtrue; } @@ -1273,7 +1293,7 @@ ossl_asn1obj_get_sn(VALUE self) val = ossl_asn1_get_value(self); if ((nid = OBJ_txt2nid(StringValueCStr(val))) != NID_undef) - ret = rb_str_new2(OBJ_nid2sn(nid)); + ret = rb_str_new2(OBJ_nid2sn(nid)); return ret; } @@ -1293,55 +1313,15 @@ ossl_asn1obj_get_ln(VALUE self) val = ossl_asn1_get_value(self); if ((nid = OBJ_txt2nid(StringValueCStr(val))) != NID_undef) - ret = rb_str_new2(OBJ_nid2ln(nid)); + ret = rb_str_new2(OBJ_nid2ln(nid)); return ret; } -/* - * call-seq: - * oid == other_oid => true or false - * - * Returns +true+ if _other_oid_ is the same as _oid_ - */ -static VALUE -ossl_asn1obj_eq(VALUE self, VALUE other) -{ - VALUE valSelf, valOther; - int nidSelf, nidOther; - - valSelf = ossl_asn1_get_value(self); - valOther = ossl_asn1_get_value(other); - - if ((nidSelf = OBJ_txt2nid(StringValueCStr(valSelf))) == NID_undef) - ossl_raise(eASN1Error, "OBJ_txt2nid"); - - if ((nidOther = OBJ_txt2nid(StringValueCStr(valOther))) == NID_undef) - ossl_raise(eASN1Error, "OBJ_txt2nid"); - - return nidSelf == nidOther ? Qtrue : Qfalse; -} - static VALUE asn1obj_get_oid_i(VALUE vobj) { - ASN1_OBJECT *a1obj = (void *)vobj; - VALUE str; - int len; - - str = rb_usascii_str_new(NULL, 127); - len = OBJ_obj2txt(RSTRING_PTR(str), RSTRING_LENINT(str), a1obj, 1); - if (len <= 0 || len == INT_MAX) - ossl_raise(eASN1Error, "OBJ_obj2txt"); - if (len > RSTRING_LEN(str)) { - /* +1 is for the \0 terminator added by OBJ_obj2txt() */ - rb_str_resize(str, len + 1); - len = OBJ_obj2txt(RSTRING_PTR(str), len + 1, a1obj, 1); - if (len <= 0) - ossl_raise(eASN1Error, "OBJ_obj2txt"); - } - rb_str_set_len(str, len); - return str; + return ossl_asn1obj_to_string_oid((const ASN1_OBJECT *)vobj); } /* @@ -1358,14 +1338,33 @@ ossl_asn1obj_get_oid(VALUE self) ASN1_OBJECT *a1obj; int state; - a1obj = obj_to_asn1obj(ossl_asn1_get_value(self)); + a1obj = ossl_to_asn1obj(ossl_asn1_get_value(self)); str = rb_protect(asn1obj_get_oid_i, (VALUE)a1obj, &state); ASN1_OBJECT_free(a1obj); if (state) - rb_jump_tag(state); + rb_jump_tag(state); return str; } +/* + * call-seq: + * oid == other_oid => true or false + * + * Returns +true+ if _other_oid_ is the same as _oid_. + */ +static VALUE +ossl_asn1obj_eq(VALUE self, VALUE other) +{ + VALUE oid1, oid2; + + if (!rb_obj_is_kind_of(other, cASN1ObjectId)) + return Qfalse; + + oid1 = ossl_asn1obj_get_oid(self); + oid2 = ossl_asn1obj_get_oid(other); + return rb_str_equal(oid1, oid2); +} + #define OSSL_ASN1_IMPL_FACTORY_METHOD(klass) \ static VALUE ossl_asn1_##klass(int argc, VALUE *argv, VALUE self)\ { return rb_funcall3(cASN1##klass, rb_intern("new"), argc, argv); } @@ -1398,13 +1397,6 @@ void Init_ossl_asn1(void) { #undef rb_intern - VALUE ary; - int i; - -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif sym_UNIVERSAL = ID2SYM(rb_intern_const("UNIVERSAL")); sym_CONTEXT_SPECIFIC = ID2SYM(rb_intern_const("CONTEXT_SPECIFIC")); @@ -1554,17 +1546,20 @@ Init_ossl_asn1(void) rb_define_module_function(mASN1, "traverse", ossl_asn1_traverse, 1); rb_define_module_function(mASN1, "decode", ossl_asn1_decode, 1); rb_define_module_function(mASN1, "decode_all", ossl_asn1_decode_all, 1); - ary = rb_ary_new(); + VALUE ary = rb_ary_new_capa(ossl_asn1_info_size); + for (int i = 0; i < ossl_asn1_info_size; i++) { + const char *name = ossl_asn1_info[i].name; + if (name[0] == '[') + continue; + rb_define_const(mASN1, name, INT2NUM(i)); + rb_ary_store(ary, i, rb_obj_freeze(rb_str_new_cstr(name))); + } + rb_obj_freeze(ary); /* * Array storing tag names at the tag's index. */ rb_define_const(mASN1, "UNIVERSAL_TAG_NAME", ary); - for(i = 0; i < ossl_asn1_info_size; i++){ - if(ossl_asn1_info[i].name[0] == '[') continue; - rb_define_const(mASN1, ossl_asn1_info[i].name, INT2NUM(i)); - rb_ary_store(ary, i, rb_str_new2(ossl_asn1_info[i].name)); - } /* Document-class: OpenSSL::ASN1::ASN1Data * @@ -1886,6 +1881,7 @@ do{\ rb_hash_aset(class_tag_map, cASN1GeneralString, INT2NUM(V_ASN1_GENERALSTRING)); rb_hash_aset(class_tag_map, cASN1UniversalString, INT2NUM(V_ASN1_UNIVERSALSTRING)); rb_hash_aset(class_tag_map, cASN1BMPString, INT2NUM(V_ASN1_BMPSTRING)); + rb_obj_freeze(class_tag_map); id_each = rb_intern_const("each"); } diff --git a/ext/openssl/ossl_asn1.h b/ext/openssl/ossl_asn1.h index 939a96ce74..b605df8f3f 100644 --- a/ext/openssl/ossl_asn1.h +++ b/ext/openssl/ossl_asn1.h @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(_OSSL_ASN1_H_) #define _OSSL_ASN1_H_ @@ -32,30 +32,26 @@ VALUE asn1integer_to_num(const ASN1_INTEGER *); ASN1_INTEGER *num_to_asn1integer(VALUE, ASN1_INTEGER *); /* + * ASN1_OBJECT conversions + */ +ASN1_OBJECT *ossl_to_asn1obj(VALUE obj); +/* + * Returns the short name if available, the dotted decimal notation otherwise. + * This is the most common way to return ASN1_OBJECT to Ruby. + */ +VALUE ossl_asn1obj_to_string(const ASN1_OBJECT *a1obj); +/* + * However, some places use long names instead. This is likely unintentional, + * but we keep the current behavior in existing methods. + */ +VALUE ossl_asn1obj_to_string_long_name(const ASN1_OBJECT *a1obj); + +/* * ASN1 module */ extern VALUE mASN1; -extern VALUE eASN1Error; extern VALUE cASN1Data; -extern VALUE cASN1Primitive; -extern VALUE cASN1Constructive; - -extern VALUE cASN1Boolean; /* BOOLEAN */ -extern VALUE cASN1Integer, cASN1Enumerated; /* INTEGER */ -extern VALUE cASN1BitString; /* BIT STRING */ -extern VALUE cASN1OctetString, cASN1UTF8String; /* STRINGs */ -extern VALUE cASN1NumericString, cASN1PrintableString; -extern VALUE cASN1T61String, cASN1VideotexString; -extern VALUE cASN1IA5String, cASN1GraphicString; -extern VALUE cASN1ISO64String, cASN1GeneralString; -extern VALUE cASN1UniversalString, cASN1BMPString; -extern VALUE cASN1Null; /* NULL */ -extern VALUE cASN1ObjectId; /* OBJECT IDENTIFIER */ -extern VALUE cASN1UTCTime, cASN1GeneralizedTime; /* TIME */ -extern VALUE cASN1Sequence, cASN1Set; /* CONSTRUCTIVE */ - -ASN1_TYPE *ossl_asn1_get_asn1type(VALUE); void Init_ossl_asn1(void); diff --git a/ext/openssl/ossl_bio.c b/ext/openssl/ossl_bio.c index 42833d901a..4edde5091d 100644 --- a/ext/openssl/ossl_bio.c +++ b/ext/openssl/ossl_bio.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -16,11 +16,11 @@ ossl_obj2bio(volatile VALUE *pobj) BIO *bio; if (RB_TYPE_P(obj, T_FILE)) - obj = rb_funcallv(obj, rb_intern("read"), 0, NULL); + obj = rb_funcallv(obj, rb_intern("read"), 0, NULL); StringValue(obj); bio = BIO_new_mem_buf(RSTRING_PTR(obj), RSTRING_LENINT(obj)); if (!bio) - ossl_raise(eOSSLError, "BIO_new_mem_buf"); + ossl_raise(eOSSLError, "BIO_new_mem_buf"); *pobj = obj; return bio; } @@ -36,7 +36,7 @@ ossl_membio2str(BIO *bio) ret = ossl_str_new(buf->data, buf->length, &state); BIO_free(bio); if (state) - rb_jump_tag(state); + rb_jump_tag(state); return ret; } diff --git a/ext/openssl/ossl_bio.h b/ext/openssl/ossl_bio.h index da68c5e5a2..1b871f1cd7 100644 --- a/ext/openssl/ossl_bio.h +++ b/ext/openssl/ossl_bio.h @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(_OSSL_BIO_H_) #define _OSSL_BIO_H_ diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c index ce0d3ec7ee..9014f2df2b 100644 --- a/ext/openssl/ossl_bn.c +++ b/ext/openssl/ossl_bn.c @@ -5,29 +5,25 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ /* modified by Michal Rokos <m.rokos@sh.cvut.cz> */ #include "ossl.h" -#ifdef HAVE_RB_EXT_RACTOR_SAFE -#include <ruby/ractor.h> -#endif - #define NewBN(klass) \ - TypedData_Wrap_Struct((klass), &ossl_bn_type, 0) + TypedData_Wrap_Struct((klass), &ossl_bn_type, 0) #define SetBN(obj, bn) do { \ - if (!(bn)) { \ - ossl_raise(rb_eRuntimeError, "BN wasn't initialized!"); \ - } \ - RTYPEDDATA_DATA(obj) = (bn); \ + if (!(bn)) { \ + ossl_raise(rb_eRuntimeError, "BN wasn't initialized!"); \ + } \ + RTYPEDDATA_DATA(obj) = (bn); \ } while (0) #define GetBN(obj, bn) do { \ - TypedData_Get_Struct((obj), BIGNUM, &ossl_bn_type, (bn)); \ - if (!(bn)) { \ - ossl_raise(rb_eRuntimeError, "BN wasn't initialized!"); \ - } \ + TypedData_Get_Struct((obj), BIGNUM, &ossl_bn_type, (bn)); \ + if (!(bn)) { \ + ossl_raise(rb_eRuntimeError, "BN wasn't initialized!"); \ + } \ } while (0) static void @@ -39,9 +35,9 @@ ossl_bn_free(void *ptr) static const rb_data_type_t ossl_bn_type = { "OpenSSL/BN", { - 0, ossl_bn_free, + 0, ossl_bn_free, }, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE, }; /* @@ -53,7 +49,7 @@ VALUE cBN; * * Generic Error for all of OpenSSL::BN (big num) */ -VALUE eBNError; +static VALUE eBNError; /* * Public @@ -65,10 +61,9 @@ ossl_bn_new(const BIGNUM *bn) VALUE obj; obj = NewBN(cBN); - newbn = bn ? BN_dup(bn) : BN_new(); - if (!newbn) { - ossl_raise(eBNError, NULL); - } + newbn = BN_dup(bn); + if (!newbn) + ossl_raise(eBNError, "BN_dup"); SetBN(obj, newbn); return obj; @@ -80,40 +75,40 @@ integer_to_bnptr(VALUE obj, BIGNUM *orig) BIGNUM *bn; if (FIXNUM_P(obj)) { - long i; - unsigned char bin[sizeof(long)]; - long n = FIX2LONG(obj); - unsigned long un = labs(n); - - for (i = sizeof(long) - 1; 0 <= i; i--) { - bin[i] = un & 0xff; - un >>= 8; - } - - bn = BN_bin2bn(bin, sizeof(bin), orig); - if (!bn) - ossl_raise(eBNError, "BN_bin2bn"); - if (n < 0) - BN_set_negative(bn, 1); + long i; + unsigned char bin[sizeof(long)]; + long n = FIX2LONG(obj); + unsigned long un = labs(n); + + for (i = sizeof(long) - 1; 0 <= i; i--) { + bin[i] = un & 0xff; + un >>= 8; + } + + bn = BN_bin2bn(bin, sizeof(bin), orig); + if (!bn) + ossl_raise(eBNError, "BN_bin2bn"); + if (n < 0) + BN_set_negative(bn, 1); } else { /* assuming Bignum */ - size_t len = rb_absint_size(obj, NULL); - unsigned char *bin; - VALUE buf; - int sign; - - if (INT_MAX < len) { - rb_raise(eBNError, "bignum too long"); - } - bin = (unsigned char*)ALLOCV_N(unsigned char, buf, len); - sign = rb_integer_pack(obj, bin, len, 1, 0, INTEGER_PACK_BIG_ENDIAN); - - bn = BN_bin2bn(bin, (int)len, orig); - ALLOCV_END(buf); - if (!bn) - ossl_raise(eBNError, "BN_bin2bn"); - if (sign < 0) - BN_set_negative(bn, 1); + size_t len = rb_absint_size(obj, NULL); + unsigned char *bin; + VALUE buf; + int sign; + + if (INT_MAX < len) { + rb_raise(eBNError, "bignum too long"); + } + bin = (unsigned char*)ALLOCV_N(unsigned char, buf, len); + sign = rb_integer_pack(obj, bin, len, 1, 0, INTEGER_PACK_BIG_ENDIAN); + + bn = BN_bin2bn(bin, (int)len, orig); + ALLOCV_END(buf); + if (!bn) + ossl_raise(eBNError, "BN_bin2bn"); + if (sign < 0) + BN_set_negative(bn, 1); } return bn; @@ -126,11 +121,11 @@ try_convert_to_bn(VALUE obj) VALUE newobj = Qnil; if (rb_obj_is_kind_of(obj, cBN)) - return obj; + return obj; if (RB_INTEGER_TYPE_P(obj)) { - newobj = NewBN(cBN); /* Handle potential mem leaks */ - bn = integer_to_bnptr(obj, NULL); - SetBN(newobj, bn); + newobj = NewBN(cBN); /* Handle potential mem leaks */ + bn = integer_to_bnptr(obj, NULL); + SetBN(newobj, bn); } return newobj; @@ -144,7 +139,7 @@ ossl_bn_value_ptr(volatile VALUE *ptr) tmp = try_convert_to_bn(*ptr); if (NIL_P(tmp)) - ossl_raise(rb_eTypeError, "Cannot convert into OpenSSL::BN"); + ossl_raise(rb_eTypeError, "Cannot convert into OpenSSL::BN"); GetBN(tmp, bn); *ptr = tmp; @@ -156,19 +151,19 @@ ossl_bn_value_ptr(volatile VALUE *ptr) */ #ifdef HAVE_RB_EXT_RACTOR_SAFE -void +static void ossl_bn_ctx_free(void *ptr) { BN_CTX *ctx = (BN_CTX *)ptr; BN_CTX_free(ctx); } -struct rb_ractor_local_storage_type ossl_bn_ctx_key_type = { +static struct rb_ractor_local_storage_type ossl_bn_ctx_key_type = { NULL, // mark ossl_bn_ctx_free, }; -rb_ractor_local_key_t ossl_bn_ctx_key; +static rb_ractor_local_key_t ossl_bn_ctx_key; BN_CTX * ossl_bn_ctx_get(void) @@ -214,7 +209,7 @@ ossl_bn_alloc(VALUE klass) VALUE obj = NewBN(klass); if (!(bn = BN_new())) { - ossl_raise(eBNError, NULL); + ossl_raise(eBNError, NULL); } SetBN(obj, bn); @@ -244,7 +239,7 @@ ossl_bn_alloc(VALUE klass) * number. * - +10+ - Decimal number representation, with a leading '-' for a negative * number. - * - +16+ - Hexadeciaml number representation, with a leading '-' for a + * - +16+ - Hexadecimal number representation, with a leading '-' for a * negative number. */ static VALUE @@ -256,57 +251,58 @@ ossl_bn_initialize(int argc, VALUE *argv, VALUE self) char *ptr; if (rb_scan_args(argc, argv, "11", &str, &bs) == 2) { - base = NUM2INT(bs); + base = NUM2INT(bs); } if (NIL_P(str)) { ossl_raise(rb_eArgError, "invalid argument"); } + rb_check_frozen(self); if (RB_INTEGER_TYPE_P(str)) { - GetBN(self, bn); - integer_to_bnptr(str, bn); + GetBN(self, bn); + integer_to_bnptr(str, bn); - return self; + return self; } if (RTEST(rb_obj_is_kind_of(str, cBN))) { - BIGNUM *other; - - GetBN(self, bn); - GetBN(str, other); /* Safe - we checked kind_of? above */ - if (!BN_copy(bn, other)) { - ossl_raise(eBNError, NULL); - } - return self; + BIGNUM *other; + + GetBN(self, bn); + GetBN(str, other); /* Safe - we checked kind_of? above */ + if (!BN_copy(bn, other)) { + ossl_raise(eBNError, NULL); + } + return self; } GetBN(self, bn); switch (base) { - case 0: + case 0: ptr = StringValuePtr(str); if (!BN_mpi2bn((unsigned char *)ptr, RSTRING_LENINT(str), bn)) { - ossl_raise(eBNError, NULL); - } - break; - case 2: + ossl_raise(eBNError, NULL); + } + break; + case 2: ptr = StringValuePtr(str); if (!BN_bin2bn((unsigned char *)ptr, RSTRING_LENINT(str), bn)) { - ossl_raise(eBNError, NULL); - } - break; - case 10: - if (!BN_dec2bn(&bn, StringValueCStr(str))) { - ossl_raise(eBNError, NULL); - } - break; - case 16: - if (!BN_hex2bn(&bn, StringValueCStr(str))) { - ossl_raise(eBNError, NULL); - } - break; - default: - ossl_raise(rb_eArgError, "invalid radix %d", base); + ossl_raise(eBNError, NULL); + } + break; + case 10: + if (!BN_dec2bn(&bn, StringValueCStr(str))) { + ossl_raise(eBNError, NULL); + } + break; + case 16: + if (!BN_hex2bn(&bn, StringValueCStr(str))) { + ossl_raise(eBNError, NULL); + } + break; + default: + ossl_raise(rb_eArgError, "invalid radix %d", base); } return self; } @@ -326,7 +322,7 @@ ossl_bn_initialize(int argc, VALUE *argv, VALUE self) * the bignum is ignored. * - +10+ - Decimal number representation, with a leading '-' for a negative * bignum. - * - +16+ - Hexadeciaml number representation, with a leading '-' for a + * - +16+ - Hexadecimal number representation, with a leading '-' for a * negative bignum. */ static VALUE @@ -338,32 +334,32 @@ ossl_bn_to_s(int argc, VALUE *argv, VALUE self) char *buf; if (rb_scan_args(argc, argv, "01", &bs) == 1) { - base = NUM2INT(bs); + base = NUM2INT(bs); } GetBN(self, bn); switch (base) { - case 0: - len = BN_bn2mpi(bn, NULL); + case 0: + len = BN_bn2mpi(bn, NULL); str = rb_str_new(0, len); - if (BN_bn2mpi(bn, (unsigned char *)RSTRING_PTR(str)) != len) - ossl_raise(eBNError, NULL); - break; - case 2: - len = BN_num_bytes(bn); + if (BN_bn2mpi(bn, (unsigned char *)RSTRING_PTR(str)) != len) + ossl_raise(eBNError, NULL); + break; + case 2: + len = BN_num_bytes(bn); str = rb_str_new(0, len); - if (BN_bn2bin(bn, (unsigned char *)RSTRING_PTR(str)) != len) - ossl_raise(eBNError, NULL); - break; - case 10: - if (!(buf = BN_bn2dec(bn))) ossl_raise(eBNError, NULL); - str = ossl_buf2str(buf, rb_long2int(strlen(buf))); - break; - case 16: - if (!(buf = BN_bn2hex(bn))) ossl_raise(eBNError, NULL); - str = ossl_buf2str(buf, rb_long2int(strlen(buf))); - break; - default: - ossl_raise(rb_eArgError, "invalid radix %d", base); + if (BN_bn2bin(bn, (unsigned char *)RSTRING_PTR(str)) != len) + ossl_raise(eBNError, NULL); + break; + case 10: + if (!(buf = BN_bn2dec(bn))) ossl_raise(eBNError, NULL); + str = ossl_buf2str(buf, rb_long2int(strlen(buf))); + break; + case 16: + if (!(buf = BN_bn2hex(bn))) ossl_raise(eBNError, NULL); + str = ossl_buf2str(buf, rb_long2int(strlen(buf))); + break; + default: + ossl_raise(rb_eArgError, "invalid radix %d", base); } return str; @@ -383,7 +379,7 @@ ossl_bn_to_i(VALUE self) GetBN(self, bn); if (!(txt = BN_bn2hex(bn))) { - ossl_raise(eBNError, NULL); + ossl_raise(eBNError, NULL); } num = rb_cstr_to_inum(txt, 16, Qtrue); OPENSSL_free(txt); @@ -401,31 +397,31 @@ static VALUE ossl_bn_coerce(VALUE self, VALUE other) { switch(TYPE(other)) { - case T_STRING: - self = ossl_bn_to_s(0, NULL, self); - break; - case T_FIXNUM: - case T_BIGNUM: - self = ossl_bn_to_i(self); - break; - default: - if (!RTEST(rb_obj_is_kind_of(other, cBN))) { - ossl_raise(rb_eTypeError, "Don't know how to coerce"); - } + case T_STRING: + self = ossl_bn_to_s(0, NULL, self); + break; + case T_FIXNUM: + case T_BIGNUM: + self = ossl_bn_to_i(self); + break; + default: + if (!RTEST(rb_obj_is_kind_of(other, cBN))) { + ossl_raise(rb_eTypeError, "Don't know how to coerce"); + } } return rb_assoc_new(other, self); } -#define BIGNUM_BOOL1(func) \ - static VALUE \ - ossl_bn_##func(VALUE self) \ - { \ - BIGNUM *bn; \ - GetBN(self, bn); \ - if (BN_##func(bn)) { \ - return Qtrue; \ - } \ - return Qfalse; \ +#define BIGNUM_BOOL1(func) \ + static VALUE \ + ossl_bn_##func(VALUE self) \ + { \ + BIGNUM *bn; \ + GetBN(self, bn); \ + if (BN_##func(bn)) { \ + return Qtrue; \ + } \ + return Qfalse; \ } /* @@ -460,27 +456,27 @@ ossl_bn_is_negative(VALUE self) GetBN(self, bn); if (BN_is_zero(bn)) - return Qfalse; + return Qfalse; return BN_is_negative(bn) ? Qtrue : Qfalse; } -#define BIGNUM_1c(func) \ - static VALUE \ - ossl_bn_##func(VALUE self) \ - { \ - BIGNUM *bn, *result; \ - VALUE obj; \ - GetBN(self, bn); \ - obj = NewBN(rb_obj_class(self)); \ - if (!(result = BN_new())) { \ - ossl_raise(eBNError, NULL); \ - } \ - if (BN_##func(result, bn, ossl_bn_ctx) <= 0) { \ - BN_free(result); \ - ossl_raise(eBNError, NULL); \ - } \ - SetBN(obj, result); \ - return obj; \ +#define BIGNUM_1c(func) \ + static VALUE \ + ossl_bn_##func(VALUE self) \ + { \ + BIGNUM *bn, *result; \ + VALUE obj; \ + GetBN(self, bn); \ + obj = NewBN(rb_obj_class(self)); \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (BN_##func(result, bn, ossl_bn_ctx) <= 0) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + SetBN(obj, result); \ + return obj; \ } /* @@ -490,23 +486,23 @@ ossl_bn_is_negative(VALUE self) */ BIGNUM_1c(sqr) -#define BIGNUM_2(func) \ - static VALUE \ - ossl_bn_##func(VALUE self, VALUE other) \ - { \ - BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ - VALUE obj; \ - GetBN(self, bn1); \ - obj = NewBN(rb_obj_class(self)); \ - if (!(result = BN_new())) { \ - ossl_raise(eBNError, NULL); \ - } \ - if (BN_##func(result, bn1, bn2) <= 0) { \ - BN_free(result); \ - ossl_raise(eBNError, NULL); \ - } \ - SetBN(obj, result); \ - return obj; \ +#define BIGNUM_2(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other) \ + { \ + BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ + VALUE obj; \ + GetBN(self, bn1); \ + obj = NewBN(rb_obj_class(self)); \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (BN_##func(result, bn1, bn2) <= 0) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + SetBN(obj, result); \ + return obj; \ } /* @@ -523,23 +519,23 @@ BIGNUM_2(add) */ BIGNUM_2(sub) -#define BIGNUM_2c(func) \ - static VALUE \ - ossl_bn_##func(VALUE self, VALUE other) \ - { \ - BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ - VALUE obj; \ - GetBN(self, bn1); \ - obj = NewBN(rb_obj_class(self)); \ - if (!(result = BN_new())) { \ - ossl_raise(eBNError, NULL); \ - } \ - if (BN_##func(result, bn1, bn2, ossl_bn_ctx) <= 0) { \ - BN_free(result); \ - ossl_raise(eBNError, NULL); \ - } \ - SetBN(obj, result); \ - return obj; \ +#define BIGNUM_2c(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other) \ + { \ + BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ + VALUE obj; \ + GetBN(self, bn1); \ + obj = NewBN(rb_obj_class(self)); \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (BN_##func(result, bn1, bn2, ossl_bn_ctx) <= 0) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + SetBN(obj, result); \ + return obj; \ } /* @@ -577,18 +573,18 @@ BIGNUM_2c(gcd) */ BIGNUM_2c(mod_sqr) -#define BIGNUM_2cr(func) \ - static VALUE \ - ossl_bn_##func(VALUE self, VALUE other) \ - { \ - BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ - VALUE obj; \ - GetBN(self, bn1); \ - obj = NewBN(rb_obj_class(self)); \ - if (!(result = BN_##func(NULL, bn1, bn2, ossl_bn_ctx))) \ - ossl_raise(eBNError, NULL); \ - SetBN(obj, result); \ - return obj; \ +#define BIGNUM_2cr(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other) \ + { \ + BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ + VALUE obj; \ + GetBN(self, bn1); \ + obj = NewBN(rb_obj_class(self)); \ + if (!(result = BN_##func(NULL, bn1, bn2, ossl_bn_ctx))) \ + ossl_raise(eBNError, NULL); \ + SetBN(obj, result); \ + return obj; \ } /* @@ -623,16 +619,16 @@ ossl_bn_div(VALUE self, VALUE other) obj1 = NewBN(klass); obj2 = NewBN(klass); if (!(r1 = BN_new())) { - ossl_raise(eBNError, NULL); + ossl_raise(eBNError, NULL); } if (!(r2 = BN_new())) { - BN_free(r1); - ossl_raise(eBNError, NULL); + BN_free(r1); + ossl_raise(eBNError, NULL); } if (!BN_div(r1, r2, bn1, bn2, ossl_bn_ctx)) { - BN_free(r1); - BN_free(r2); - ossl_raise(eBNError, NULL); + BN_free(r1); + BN_free(r2); + ossl_raise(eBNError, NULL); } SetBN(obj1, r1); SetBN(obj2, r2); @@ -640,24 +636,24 @@ ossl_bn_div(VALUE self, VALUE other) return rb_ary_new3(2, obj1, obj2); } -#define BIGNUM_3c(func) \ - static VALUE \ - ossl_bn_##func(VALUE self, VALUE other1, VALUE other2) \ - { \ - BIGNUM *bn1, *bn2 = GetBNPtr(other1); \ - BIGNUM *bn3 = GetBNPtr(other2), *result; \ - VALUE obj; \ - GetBN(self, bn1); \ - obj = NewBN(rb_obj_class(self)); \ - if (!(result = BN_new())) { \ - ossl_raise(eBNError, NULL); \ - } \ - if (BN_##func(result, bn1, bn2, bn3, ossl_bn_ctx) <= 0) { \ - BN_free(result); \ - ossl_raise(eBNError, NULL); \ - } \ - SetBN(obj, result); \ - return obj; \ +#define BIGNUM_3c(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other1, VALUE other2) \ + { \ + BIGNUM *bn1, *bn2 = GetBNPtr(other1); \ + BIGNUM *bn3 = GetBNPtr(other2), *result; \ + VALUE obj; \ + GetBN(self, bn1); \ + obj = NewBN(rb_obj_class(self)); \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (BN_##func(result, bn1, bn2, bn3, ossl_bn_ctx) <= 0) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + SetBN(obj, result); \ + return obj; \ } /* @@ -688,16 +684,17 @@ BIGNUM_3c(mod_mul) */ BIGNUM_3c(mod_exp) -#define BIGNUM_BIT(func) \ - static VALUE \ - ossl_bn_##func(VALUE self, VALUE bit) \ - { \ - BIGNUM *bn; \ - GetBN(self, bn); \ - if (BN_##func(bn, NUM2INT(bit)) <= 0) { \ - ossl_raise(eBNError, NULL); \ - } \ - return self; \ +#define BIGNUM_BIT(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE bit) \ + { \ + BIGNUM *bn; \ + rb_check_frozen(self); \ + GetBN(self, bn); \ + if (BN_##func(bn, NUM2INT(bit)) <= 0) { \ + ossl_raise(eBNError, NULL); \ + } \ + return self; \ } /* @@ -736,30 +733,30 @@ ossl_bn_is_bit_set(VALUE self, VALUE bit) b = NUM2INT(bit); GetBN(self, bn); if (BN_is_bit_set(bn, b)) { - return Qtrue; + return Qtrue; } return Qfalse; } -#define BIGNUM_SHIFT(func) \ - static VALUE \ - ossl_bn_##func(VALUE self, VALUE bits) \ - { \ - BIGNUM *bn, *result; \ - int b; \ - VALUE obj; \ - b = NUM2INT(bits); \ - GetBN(self, bn); \ - obj = NewBN(rb_obj_class(self)); \ - if (!(result = BN_new())) { \ - ossl_raise(eBNError, NULL); \ - } \ - if (BN_##func(result, bn, b) <= 0) { \ - BN_free(result); \ - ossl_raise(eBNError, NULL); \ - } \ - SetBN(obj, result); \ - return obj; \ +#define BIGNUM_SHIFT(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE bits) \ + { \ + BIGNUM *bn, *result; \ + int b; \ + VALUE obj; \ + b = NUM2INT(bits); \ + GetBN(self, bn); \ + obj = NewBN(rb_obj_class(self)); \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (BN_##func(result, bn, b) <= 0) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + SetBN(obj, result); \ + return obj; \ } /* @@ -776,17 +773,18 @@ BIGNUM_SHIFT(lshift) */ BIGNUM_SHIFT(rshift) -#define BIGNUM_SELF_SHIFT(func) \ - static VALUE \ - ossl_bn_self_##func(VALUE self, VALUE bits) \ - { \ - BIGNUM *bn; \ - int b; \ - b = NUM2INT(bits); \ - GetBN(self, bn); \ - if (BN_##func(bn, bn, b) <= 0) \ - ossl_raise(eBNError, NULL); \ - return self; \ +#define BIGNUM_SELF_SHIFT(func) \ + static VALUE \ + ossl_bn_self_##func(VALUE self, VALUE bits) \ + { \ + BIGNUM *bn; \ + int b; \ + rb_check_frozen(self); \ + b = NUM2INT(bits); \ + GetBN(self, bn); \ + if (BN_##func(bn, bn, b) <= 0) \ + ossl_raise(eBNError, NULL); \ + return self; \ } /* @@ -888,32 +886,32 @@ ossl_bn_s_generate_prime(int argc, VALUE *argv, VALUE klass) num = NUM2INT(vnum); if (vsafe == Qfalse) { - safe = 0; + safe = 0; } if (!NIL_P(vadd)) { - add = GetBNPtr(vadd); - rem = NIL_P(vrem) ? NULL : GetBNPtr(vrem); + add = GetBNPtr(vadd); + rem = NIL_P(vrem) ? NULL : GetBNPtr(vrem); } obj = NewBN(klass); if (!(result = BN_new())) { - ossl_raise(eBNError, NULL); + ossl_raise(eBNError, NULL); } if (!BN_generate_prime_ex(result, num, safe, add, rem, NULL)) { - BN_free(result); - ossl_raise(eBNError, NULL); + BN_free(result); + ossl_raise(eBNError, NULL); } SetBN(obj, result); return obj; } -#define BIGNUM_NUM(func) \ - static VALUE \ - ossl_bn_##func(VALUE self) \ - { \ - BIGNUM *bn; \ - GetBN(self, bn); \ - return INT2NUM(BN_##func(bn)); \ +#define BIGNUM_NUM(func) \ + static VALUE \ + ossl_bn_##func(VALUE self) \ + { \ + BIGNUM *bn; \ + GetBN(self, bn); \ + return INT2NUM(BN_##func(bn)); \ } /* @@ -930,6 +928,7 @@ BIGNUM_NUM(num_bytes) */ BIGNUM_NUM(num_bits) +/* :nodoc: */ static VALUE ossl_bn_copy(VALUE self, VALUE other) { @@ -943,7 +942,7 @@ ossl_bn_copy(VALUE self, VALUE other) bn2 = GetBNPtr(other); if (!BN_copy(bn1, bn2)) { - ossl_raise(eBNError, NULL); + ossl_raise(eBNError, NULL); } return self; } @@ -962,7 +961,7 @@ ossl_bn_uplus(VALUE self) obj = NewBN(cBN); bn2 = BN_dup(bn1); if (!bn2) - ossl_raise(eBNError, "BN_dup"); + ossl_raise(eBNError, "BN_dup"); SetBN(obj, bn2); return obj; @@ -982,7 +981,7 @@ ossl_bn_uminus(VALUE self) obj = NewBN(cBN); bn2 = BN_dup(bn1); if (!bn2) - ossl_raise(eBNError, "BN_dup"); + ossl_raise(eBNError, "BN_dup"); SetBN(obj, bn2); BN_set_negative(bn2, !BN_is_negative(bn2)); @@ -1007,13 +1006,13 @@ ossl_bn_abs(VALUE self) } } -#define BIGNUM_CMP(func) \ - static VALUE \ - ossl_bn_##func(VALUE self, VALUE other) \ - { \ - BIGNUM *bn1, *bn2 = GetBNPtr(other); \ - GetBN(self, bn1); \ - return INT2NUM(BN_##func(bn1, bn2)); \ +#define BIGNUM_CMP(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other) \ + { \ + BIGNUM *bn1, *bn2 = GetBNPtr(other); \ + GetBN(self, bn1); \ + return INT2NUM(BN_##func(bn1, bn2)); \ } /* @@ -1050,11 +1049,11 @@ ossl_bn_eq(VALUE self, VALUE other) GetBN(self, bn1); other = try_convert_to_bn(other); if (NIL_P(other)) - return Qfalse; + return Qfalse; GetBN(other, bn2); if (!BN_cmp(bn1, bn2)) { - return Qtrue; + return Qtrue; } return Qfalse; } @@ -1073,7 +1072,7 @@ ossl_bn_eql(VALUE self, VALUE other) BIGNUM *bn1, *bn2; if (!rb_obj_is_kind_of(other, cBN)) - return Qfalse; + return Qfalse; GetBN(self, bn1); GetBN(other, bn2); @@ -1100,8 +1099,8 @@ ossl_bn_hash(VALUE self) len = BN_num_bytes(bn); buf = ALLOCV(tmp, len); if (BN_bn2bin(bn, buf) != len) { - ALLOCV_END(tmp); - ossl_raise(eBNError, "BN_bn2bin"); + ALLOCV_END(tmp); + ossl_raise(eBNError, "BN_bn2bin"); } hash = ST2FIX(rb_memhash(buf, len)); @@ -1191,6 +1190,7 @@ ossl_bn_set_flags(VALUE self, VALUE arg) BIGNUM *bn; GetBN(self, bn); + rb_check_frozen(self); BN_set_flags(bn, NUM2INT(arg)); return Qnil; } @@ -1202,11 +1202,6 @@ ossl_bn_set_flags(VALUE self, VALUE arg) void Init_ossl_bn(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - #ifdef HAVE_RB_EXT_RACTOR_SAFE ossl_bn_ctx_key = rb_ractor_local_storage_ptr_newkey(&ossl_bn_ctx_key_type); #else diff --git a/ext/openssl/ossl_bn.h b/ext/openssl/ossl_bn.h index 1cc041fc22..0c186bd1c5 100644 --- a/ext/openssl/ossl_bn.h +++ b/ext/openssl/ossl_bn.h @@ -5,13 +5,12 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(_OSSL_BN_H_) #define _OSSL_BN_H_ extern VALUE cBN; -extern VALUE eBNError; BN_CTX *ossl_bn_ctx_get(void); #define ossl_bn_ctx ossl_bn_ctx_get() diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c index 110610e1f9..f3cd247c8f 100644 --- a/ext/openssl/ossl_cipher.c +++ b/ext/openssl/ossl_cipher.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -14,7 +14,7 @@ #define AllocCipher(obj, ctx) do { \ (ctx) = EVP_CIPHER_CTX_new(); \ if (!(ctx)) \ - ossl_raise(rb_eRuntimeError, NULL); \ + ossl_raise(rb_eRuntimeError, NULL); \ RTYPEDDATA_DATA(obj) = (ctx); \ } while (0) #define GetCipherInit(obj, ctx) do { \ @@ -23,16 +23,17 @@ #define GetCipher(obj, ctx) do { \ GetCipherInit((obj), (ctx)); \ if (!(ctx)) { \ - ossl_raise(rb_eRuntimeError, "Cipher not initialized!"); \ + ossl_raise(rb_eRuntimeError, "Cipher not initialized!"); \ } \ } while (0) /* * Classes */ -VALUE cCipher; -VALUE eCipherError; -static ID id_auth_tag_len, id_key_set; +static VALUE cCipher; +static VALUE eCipherError; +static VALUE eAuthTagError; +static ID id_auth_tag_len, id_key_set, id_cipher_holder; static VALUE ossl_cipher_alloc(VALUE klass); static void ossl_cipher_free(void *ptr); @@ -40,35 +41,63 @@ static void ossl_cipher_free(void *ptr); static const rb_data_type_t ossl_cipher_type = { "OpenSSL/Cipher", { - 0, ossl_cipher_free, + 0, ossl_cipher_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; +#ifdef OSSL_USE_PROVIDER +static void +ossl_evp_cipher_free(void *ptr) +{ + // This is safe to call against const EVP_CIPHER * returned by + // EVP_get_cipherbyname() + EVP_CIPHER_free(ptr); +} + +static const rb_data_type_t ossl_evp_cipher_holder_type = { + "OpenSSL/EVP_CIPHER", + { + .dfree = ossl_evp_cipher_free, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, +}; +#endif + /* * PUBLIC */ const EVP_CIPHER * -ossl_evp_get_cipherbyname(VALUE obj) +ossl_evp_cipher_fetch(VALUE obj, volatile VALUE *holder) { + *holder = Qnil; if (rb_obj_is_kind_of(obj, cCipher)) { - EVP_CIPHER_CTX *ctx; - - GetCipher(obj, ctx); - - return EVP_CIPHER_CTX_cipher(ctx); + EVP_CIPHER_CTX *ctx; + GetCipher(obj, ctx); + EVP_CIPHER *cipher = (EVP_CIPHER *)EVP_CIPHER_CTX_cipher(ctx); +#ifdef OSSL_USE_PROVIDER + *holder = TypedData_Wrap_Struct(0, &ossl_evp_cipher_holder_type, NULL); + if (!EVP_CIPHER_up_ref(cipher)) + ossl_raise(eCipherError, "EVP_CIPHER_up_ref"); + RTYPEDDATA_DATA(*holder) = cipher; +#endif + return cipher; } - else { - const EVP_CIPHER *cipher; - - StringValueCStr(obj); - cipher = EVP_get_cipherbyname(RSTRING_PTR(obj)); - if (!cipher) - ossl_raise(rb_eArgError, - "unsupported cipher algorithm: %"PRIsVALUE, obj); - return cipher; + const char *name = StringValueCStr(obj); + EVP_CIPHER *cipher = (EVP_CIPHER *)EVP_get_cipherbyname(name); +#ifdef OSSL_USE_PROVIDER + if (!cipher) { + ossl_clear_error(); + *holder = TypedData_Wrap_Struct(0, &ossl_evp_cipher_holder_type, NULL); + cipher = EVP_CIPHER_fetch(NULL, name, NULL); + RTYPEDDATA_DATA(*holder) = cipher; } +#endif + if (!cipher) + ossl_raise(eCipherError, "unsupported cipher algorithm: %"PRIsVALUE, + obj); + return cipher; } VALUE @@ -77,10 +106,13 @@ ossl_cipher_new(const EVP_CIPHER *cipher) VALUE ret; EVP_CIPHER_CTX *ctx; + // NOTE: This does not set id_cipher_holder because this function should + // only be called from ossl_engine.c, which will not use any + // reference-counted ciphers. ret = ossl_cipher_alloc(cCipher); AllocCipher(ret, ctx); if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, NULL); return ret; } @@ -113,23 +145,22 @@ ossl_cipher_initialize(VALUE self, VALUE str) { EVP_CIPHER_CTX *ctx; const EVP_CIPHER *cipher; - char *name; + VALUE cipher_holder; - name = StringValueCStr(str); GetCipherInit(self, ctx); if (ctx) { - ossl_raise(rb_eRuntimeError, "Cipher already initialized!"); + ossl_raise(rb_eRuntimeError, "Cipher already initialized!"); } + cipher = ossl_evp_cipher_fetch(str, &cipher_holder); AllocCipher(self, ctx); - if (!(cipher = EVP_get_cipherbyname(name))) { - ossl_raise(rb_eRuntimeError, "unsupported cipher algorithm (%"PRIsVALUE")", str); - } if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, "EVP_CipherInit_ex"); + rb_ivar_set(self, id_cipher_holder, cipher_holder); return self; } +/* :nodoc: */ static VALUE ossl_cipher_copy(VALUE self, VALUE other) { @@ -140,11 +171,11 @@ ossl_cipher_copy(VALUE self, VALUE other) GetCipherInit(self, ctx1); if (!ctx1) { - AllocCipher(self, ctx1); + AllocCipher(self, ctx1); } GetCipher(other, ctx2); if (EVP_CIPHER_CTX_copy(ctx1, ctx2) != 1) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, NULL); return self; } @@ -169,8 +200,8 @@ ossl_s_ciphers(VALUE self) ary = rb_ary_new(); OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH, - add_cipher_name_to_ary, - (void*)ary); + add_cipher_name_to_ary, + (void*)ary); return ary; } @@ -191,53 +222,22 @@ ossl_cipher_reset(VALUE self) GetCipher(self, ctx); if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, -1) != 1) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, NULL); return self; } static VALUE -ossl_cipher_init(int argc, VALUE *argv, VALUE self, int mode) +ossl_cipher_init(VALUE self, int enc) { EVP_CIPHER_CTX *ctx; - unsigned char key[EVP_MAX_KEY_LENGTH], *p_key = NULL; - unsigned char iv[EVP_MAX_IV_LENGTH], *p_iv = NULL; - VALUE pass, init_v; - - if(rb_scan_args(argc, argv, "02", &pass, &init_v) > 0){ - /* - * oops. this code mistakes salt for IV. - * We deprecated the arguments for this method, but we decided - * keeping this behaviour for backward compatibility. - */ - VALUE cname = rb_class_path(rb_obj_class(self)); - rb_warn("arguments for %"PRIsVALUE"#encrypt and %"PRIsVALUE"#decrypt were deprecated; " - "use %"PRIsVALUE"#pkcs5_keyivgen to derive key and IV", - cname, cname, cname); - StringValue(pass); - GetCipher(self, ctx); - if (NIL_P(init_v)) memcpy(iv, "OpenSSL for Ruby rulez!", sizeof(iv)); - else{ - StringValue(init_v); - if (EVP_MAX_IV_LENGTH > RSTRING_LEN(init_v)) { - memset(iv, 0, EVP_MAX_IV_LENGTH); - memcpy(iv, RSTRING_PTR(init_v), RSTRING_LEN(init_v)); - } - else memcpy(iv, RSTRING_PTR(init_v), sizeof(iv)); - } - EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), EVP_md5(), iv, - (unsigned char *)RSTRING_PTR(pass), RSTRING_LENINT(pass), 1, key, NULL); - p_key = key; - p_iv = iv; - } - else { - GetCipher(self, ctx); - } - if (EVP_CipherInit_ex(ctx, NULL, NULL, p_key, p_iv, mode) != 1) { - ossl_raise(eCipherError, NULL); + + GetCipher(self, ctx); + if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, enc) != 1) { + ossl_raise(eCipherError, "EVP_CipherInit_ex"); } - rb_ivar_set(self, id_key_set, p_key ? Qtrue : Qfalse); + rb_ivar_set(self, id_key_set, Qfalse); return self; } @@ -248,16 +248,15 @@ ossl_cipher_init(int argc, VALUE *argv, VALUE self, int mode) * * Initializes the Cipher for encryption. * - * Make sure to call Cipher#encrypt or Cipher#decrypt before using any of the - * following methods: - * * [#key=, #iv=, #random_key, #random_iv, #pkcs5_keyivgen] + * Make sure to call either #encrypt or #decrypt before using the Cipher for + * any operation or setting any parameters. * * Internally calls EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, 1). */ static VALUE -ossl_cipher_encrypt(int argc, VALUE *argv, VALUE self) +ossl_cipher_encrypt(VALUE self) { - return ossl_cipher_init(argc, argv, self, 1); + return ossl_cipher_init(self, 1); } /* @@ -266,16 +265,15 @@ ossl_cipher_encrypt(int argc, VALUE *argv, VALUE self) * * Initializes the Cipher for decryption. * - * Make sure to call Cipher#encrypt or Cipher#decrypt before using any of the - * following methods: - * * [#key=, #iv=, #random_key, #random_iv, #pkcs5_keyivgen] + * Make sure to call either #encrypt or #decrypt before using the Cipher for + * any operation or setting any parameters. * * Internally calls EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, 0). */ static VALUE -ossl_cipher_decrypt(int argc, VALUE *argv, VALUE self) +ossl_cipher_decrypt(VALUE self) { - return ossl_cipher_init(argc, argv, self, 0); + return ossl_cipher_init(self, 0); } /* @@ -284,46 +282,42 @@ ossl_cipher_decrypt(int argc, VALUE *argv, VALUE self) * * Generates and sets the key/IV based on a password. * - * *WARNING*: This method is only PKCS5 v1.5 compliant when using RC2, RC4-40, - * or DES with MD5 or SHA1. Using anything else (like AES) will generate the - * key/iv using an OpenSSL specific method. This method is deprecated and - * should no longer be used. Use a PKCS5 v2 key generation method from - * OpenSSL::PKCS5 instead. + * *WARNING*: This method is deprecated and should not be used. This method + * corresponds to EVP_BytesToKey(), a non-standard OpenSSL extension of the + * legacy PKCS #5 v1.5 key derivation function. See OpenSSL::KDF for other + * options to derive keys from passwords. * * === Parameters * * _salt_ must be an 8 byte string if provided. * * _iterations_ is an integer with a default of 2048. * * _digest_ is a Digest object that defaults to 'MD5' - * - * A minimum of 1000 iterations is recommended. - * */ static VALUE ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self) { EVP_CIPHER_CTX *ctx; const EVP_MD *digest; - VALUE vpass, vsalt, viter, vdigest; + VALUE vpass, vsalt, viter, vdigest, md_holder; unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH], *salt = NULL; int iter; rb_scan_args(argc, argv, "13", &vpass, &vsalt, &viter, &vdigest); StringValue(vpass); if(!NIL_P(vsalt)){ - StringValue(vsalt); - if(RSTRING_LEN(vsalt) != PKCS5_SALT_LEN) - ossl_raise(eCipherError, "salt must be an 8-octet string"); - salt = (unsigned char *)RSTRING_PTR(vsalt); + StringValue(vsalt); + if(RSTRING_LEN(vsalt) != PKCS5_SALT_LEN) + ossl_raise(eCipherError, "salt must be an 8-octet string"); + salt = (unsigned char *)RSTRING_PTR(vsalt); } iter = NIL_P(viter) ? 2048 : NUM2INT(viter); if (iter <= 0) - rb_raise(rb_eArgError, "iterations must be a positive integer"); - digest = NIL_P(vdigest) ? EVP_md5() : ossl_evp_get_digestbyname(vdigest); + rb_raise(rb_eArgError, "iterations must be a positive integer"); + digest = NIL_P(vdigest) ? EVP_md5() : ossl_evp_md_fetch(vdigest, &md_holder); GetCipher(self, ctx); EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), digest, salt, - (unsigned char *)RSTRING_PTR(vpass), RSTRING_LENINT(vpass), iter, key, iv); + (unsigned char *)RSTRING_PTR(vpass), RSTRING_LENINT(vpass), iter, key, iv); if (EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, -1) != 1) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, NULL); OPENSSL_cleanse(key, sizeof key); OPENSSL_cleanse(iv, sizeof iv); @@ -334,25 +328,25 @@ ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self) static int ossl_cipher_update_long(EVP_CIPHER_CTX *ctx, unsigned char *out, long *out_len_ptr, - const unsigned char *in, long in_len) + const unsigned char *in, long in_len) { int out_part_len; int limit = INT_MAX / 2 + 1; long out_len = 0; do { - int in_part_len = in_len > limit ? limit : (int)in_len; + int in_part_len = in_len > limit ? limit : (int)in_len; - if (!EVP_CipherUpdate(ctx, out ? (out + out_len) : 0, - &out_part_len, in, in_part_len)) - return 0; + if (!EVP_CipherUpdate(ctx, out ? (out + out_len) : 0, + &out_part_len, in, in_part_len)) + return 0; - out_len += out_part_len; - in += in_part_len; + out_len += out_part_len; + in += in_part_len; } while ((in_len -= limit) > 0); if (out_len_ptr) - *out_len_ptr = out_len; + *out_len_ptr = out_len; return 1; } @@ -368,6 +362,9 @@ ossl_cipher_update_long(EVP_CIPHER_CTX *ctx, unsigned char *out, long *out_len_p * * If _buffer_ is given, the encryption/decryption result will be written to * it. _buffer_ will be resized automatically. + * + * *NOTE*: When decrypting using an AEAD cipher, the integrity of the output + * is not verified until #final has been called. */ static VALUE ossl_cipher_update(int argc, VALUE *argv, VALUE self) @@ -380,28 +377,43 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "11", &data, &str); if (!RTEST(rb_attr_get(self, id_key_set))) - ossl_raise(eCipherError, "key not set"); + ossl_raise(eCipherError, "key not set"); StringValue(data); in = (unsigned char *)RSTRING_PTR(data); in_len = RSTRING_LEN(data); GetCipher(self, ctx); - out_len = in_len+EVP_CIPHER_CTX_block_size(ctx); - if (out_len <= 0) { - ossl_raise(rb_eRangeError, - "data too big to make output buffer: %ld bytes", in_len); + + /* + * As of OpenSSL 3.2, there is no reliable way to determine the required + * output buffer size for arbitrary cipher modes. + * https://github.com/openssl/openssl/issues/22628 + * + * in_len+block_size is usually sufficient, but AES key wrap with padding + * ciphers require in_len+15 even though they have a block size of 8 bytes. + * + * Using EVP_MAX_BLOCK_LENGTH (32) as a safe upper bound for ciphers + * currently implemented in OpenSSL, but this can change in the future. + */ + if (in_len > LONG_MAX - EVP_MAX_BLOCK_LENGTH) { + ossl_raise(rb_eRangeError, + "data too big to make output buffer: %ld bytes", in_len); } + out_len = in_len + EVP_MAX_BLOCK_LENGTH; - if (NIL_P(str)) { - str = rb_str_new(0, out_len); - } else { + if (NIL_P(str)) + str = rb_str_buf_new(out_len); + else { StringValue(str); - rb_str_resize(str, out_len); + if ((long)rb_str_capacity(str) >= out_len) + rb_str_modify(str); + else + rb_str_modify_expand(str, out_len - RSTRING_LEN(str)); } - if (!ossl_cipher_update_long(ctx, (unsigned char *)RSTRING_PTR(str), &out_len, in, in_len)) - ossl_raise(eCipherError, NULL); - assert(out_len < RSTRING_LEN(str)); + if (!ossl_cipher_update_long(ctx, (unsigned char *)RSTRING_PTR(str), + &out_len, in, in_len)) + ossl_raise(eCipherError, "EVP_CipherUpdate"); rb_str_set_len(str, out_len); return str; @@ -412,14 +424,17 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self) * cipher.final -> string * * Returns the remaining data held in the cipher object. Further calls to - * Cipher#update or Cipher#final will return garbage. This call should always + * Cipher#update or Cipher#final are invalid. This call should always * be made as the last call of an encryption or decryption operation, after * having fed the entire plaintext or ciphertext to the Cipher instance. * - * If an authenticated cipher was used, a CipherError is raised if the tag - * could not be authenticated successfully. Only call this method after - * setting the authentication tag and passing the entire contents of the - * ciphertext into the cipher. + * When encrypting using an AEAD cipher, the authentication tag can be + * retrieved by #auth_tag after #final has been called. + * + * When decrypting using an AEAD cipher, this method will verify the integrity + * of the ciphertext and the associated data with the authentication tag, + * which must be set by #auth_tag= prior to calling this method. + * If the verification fails, CipherError will be raised. */ static VALUE ossl_cipher_final(VALUE self) @@ -430,9 +445,17 @@ ossl_cipher_final(VALUE self) GetCipher(self, ctx); str = rb_str_new(0, EVP_CIPHER_CTX_block_size(ctx)); - if (!EVP_CipherFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), &out_len)) - ossl_raise(eCipherError, NULL); - assert(out_len <= RSTRING_LEN(str)); + if (!EVP_CipherFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), &out_len)) { + /* For AEAD ciphers, this is likely an authentication failure */ + if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) { + /* For AEAD ciphers, EVP_CipherFinal_ex failures are authentication tag verification failures */ + ossl_raise(eAuthTagError, "AEAD authentication tag verification failed"); + } + else { + /* For non-AEAD ciphers */ + ossl_raise(eCipherError, "cipher final failed"); + } + } rb_str_set_len(str, out_len); return str; @@ -442,8 +465,8 @@ ossl_cipher_final(VALUE self) * call-seq: * cipher.name -> string * - * Returns the name of the cipher which may differ slightly from the original - * name provided. + * Returns the short name of the cipher which may differ slightly from the + * original name provided. */ static VALUE ossl_cipher_name(VALUE self) @@ -457,7 +480,7 @@ ossl_cipher_name(VALUE self) /* * call-seq: - * cipher.key = string -> string + * cipher.key = string * * Sets the cipher key. To generate a key, you should either use a secure * random byte string or, if the key is to be derived from a password, you @@ -465,6 +488,8 @@ ossl_cipher_name(VALUE self) * generate a secure random-based key, Cipher#random_key may be used. * * Only call this method after calling Cipher#encrypt or Cipher#decrypt. + * + * See also the man page EVP_CipherInit_ex(3). */ static VALUE ossl_cipher_set_key(VALUE self, VALUE key) @@ -477,10 +502,10 @@ ossl_cipher_set_key(VALUE self, VALUE key) key_len = EVP_CIPHER_CTX_key_length(ctx); if (RSTRING_LEN(key) != key_len) - ossl_raise(rb_eArgError, "key must be %d bytes", key_len); + ossl_raise(rb_eArgError, "key must be %d bytes", key_len); if (EVP_CipherInit_ex(ctx, NULL, NULL, (unsigned char *)RSTRING_PTR(key), NULL, -1) != 1) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, NULL); rb_ivar_set(self, id_key_set, Qtrue); @@ -489,15 +514,21 @@ ossl_cipher_set_key(VALUE self, VALUE key) /* * call-seq: - * cipher.iv = string -> string + * cipher.iv = string * * Sets the cipher IV. Please note that since you should never be using ECB * mode, an IV is always explicitly required and should be set prior to - * encryption. The IV itself can be safely transmitted in public, but it - * should be unpredictable to prevent certain kinds of attacks. You may use - * Cipher#random_iv to create a secure random IV. + * encryption. The IV itself can be safely transmitted in public. * - * Only call this method after calling Cipher#encrypt or Cipher#decrypt. + * This method expects the String to have the length equal to #iv_len. To use + * a different IV length with an AEAD cipher, #iv_len= must be set prior to + * calling this method. + * + * *NOTE*: In OpenSSL API conventions, the IV value may correspond to the + * "nonce" instead in some cipher modes. Refer to the OpenSSL man pages for + * details. + * + * See also the man page EVP_CipherInit_ex(3). */ static VALUE ossl_cipher_set_iv(VALUE self, VALUE iv) @@ -509,14 +540,14 @@ ossl_cipher_set_iv(VALUE self, VALUE iv) GetCipher(self, ctx); if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) - iv_len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx); + iv_len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx); if (!iv_len) - iv_len = EVP_CIPHER_CTX_iv_length(ctx); + iv_len = EVP_CIPHER_CTX_iv_length(ctx); if (RSTRING_LEN(iv) != iv_len) - ossl_raise(rb_eArgError, "iv must be %d bytes", iv_len); + ossl_raise(rb_eArgError, "iv must be %d bytes", iv_len); if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, (unsigned char *)RSTRING_PTR(iv), -1) != 1) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, NULL); return iv; } @@ -525,8 +556,7 @@ ossl_cipher_set_iv(VALUE self, VALUE iv) * call-seq: * cipher.authenticated? -> true | false * - * Indicated whether this Cipher instance uses an Authenticated Encryption - * mode. + * Indicates whether this Cipher instance uses an AEAD mode. */ static VALUE ossl_cipher_is_authenticated(VALUE self) @@ -540,21 +570,23 @@ ossl_cipher_is_authenticated(VALUE self) /* * call-seq: - * cipher.auth_data = string -> string + * cipher.auth_data = string + * + * Sets additional authenticated data (AAD), also called associated data, for + * this Cipher. This method is available for AEAD ciphers. * - * Sets the cipher's additional authenticated data. This field must be - * set when using AEAD cipher modes such as GCM or CCM. If no associated - * data shall be used, this method must *still* be called with a value of "". * The contents of this field should be non-sensitive data which will be * added to the ciphertext to generate the authentication tag which validates * the contents of the ciphertext. * - * The AAD must be set prior to encryption or decryption. In encryption mode, - * it must be set after calling Cipher#encrypt and setting Cipher#key= and - * Cipher#iv=. When decrypting, the authenticated data must be set after key, - * iv and especially *after* the authentication tag has been set. I.e. set it - * only after calling Cipher#decrypt, Cipher#key=, Cipher#iv= and - * Cipher#auth_tag= first. + * This method must be called after #key= and #iv= have been set, but before + * starting actual encryption or decryption with #update. In some cipher modes, + * #auth_tag_len= and #ccm_data_len= may also need to be called before this + * method. + * + * See also the "AEAD Interface" section of the man page EVP_EncryptInit(3). + * This method internally calls EVP_CipherUpdate() with the output buffer + * set to NULL. */ static VALUE ossl_cipher_set_auth_data(VALUE self, VALUE data) @@ -570,7 +602,7 @@ ossl_cipher_set_auth_data(VALUE self, VALUE data) GetCipher(self, ctx); if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) - ossl_raise(eCipherError, "AEAD not supported by this cipher"); + ossl_raise(eCipherError, "AEAD not supported by this cipher"); if (!ossl_cipher_update_long(ctx, NULL, &out_len, in, in_len)) ossl_raise(eCipherError, "couldn't set additional authenticated data"); @@ -582,16 +614,17 @@ ossl_cipher_set_auth_data(VALUE self, VALUE data) * call-seq: * cipher.auth_tag(tag_len = 16) -> String * - * Gets the authentication tag generated by Authenticated Encryption Cipher - * modes (GCM for example). This tag may be stored along with the ciphertext, - * then set on the decryption cipher to authenticate the contents of the - * ciphertext against changes. If the optional integer parameter _tag_len_ is - * given, the returned tag will be _tag_len_ bytes long. If the parameter is - * omitted, the default length of 16 bytes or the length previously set by - * #auth_tag_len= will be used. For maximum security, the longest possible - * should be chosen. + * Gets the generated authentication tag. This method is available for AEAD + * ciphers, and should be called after encryption has been finalized by calling + * #final. * - * The tag may only be retrieved after calling Cipher#final. + * The returned tag will be _tag_len_ bytes long. Some cipher modes require + * the desired length in advance using a separate call to #auth_tag_len=, + * before starting encryption. + * + * See also the "AEAD Interface" section of the man page EVP_EncryptInit(3). + * This method internally calls EVP_CIPHER_CTX_ctrl() with + * EVP_CTRL_AEAD_GET_TAG. */ static VALUE ossl_cipher_get_auth_tag(int argc, VALUE *argv, VALUE self) @@ -602,34 +635,42 @@ ossl_cipher_get_auth_tag(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "01", &vtag_len); if (NIL_P(vtag_len)) - vtag_len = rb_attr_get(self, id_auth_tag_len); + vtag_len = rb_attr_get(self, id_auth_tag_len); if (!NIL_P(vtag_len)) - tag_len = NUM2INT(vtag_len); + tag_len = NUM2INT(vtag_len); GetCipher(self, ctx); if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) - ossl_raise(eCipherError, "authentication tag not supported by this cipher"); + ossl_raise(eCipherError, "authentication tag not supported by this cipher"); ret = rb_str_new(NULL, tag_len); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, RSTRING_PTR(ret))) - ossl_raise(eCipherError, "retrieving the authentication tag failed"); + ossl_raise(eCipherError, "retrieving the authentication tag failed"); return ret; } /* * call-seq: - * cipher.auth_tag = string -> string + * cipher.auth_tag = string * * Sets the authentication tag to verify the integrity of the ciphertext. - * This can be called only when the cipher supports AE. The tag must be set - * after calling Cipher#decrypt, Cipher#key= and Cipher#iv=, but before - * calling Cipher#final. After all decryption is performed, the tag is - * verified automatically in the call to Cipher#final. * - * For OCB mode, the tag length must be supplied with #auth_tag_len= - * beforehand. + * The authentication tag must be set before #final is called. The tag is + * verified during the #final call. + * + * Note that, for CCM mode and OCB mode, the expected length of the tag must + * be set before starting decryption by a separate call to #auth_tag_len=. + * The content of the tag can be provided at any time before #final is called. + * + * *NOTE*: The caller must ensure that the String passed to this method has + * the desired length. Some cipher modes support variable tag lengths, and + * this method may accept a truncated tag without raising an exception. + * + * See also the "AEAD Interface" section of the man page EVP_EncryptInit(3). + * This method internally calls EVP_CIPHER_CTX_ctrl() with + * EVP_CTRL_AEAD_SET_TAG. */ static VALUE ossl_cipher_set_auth_tag(VALUE self, VALUE vtag) @@ -644,24 +685,27 @@ ossl_cipher_set_auth_tag(VALUE self, VALUE vtag) GetCipher(self, ctx); if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) - ossl_raise(eCipherError, "authentication tag not supported by this cipher"); + ossl_raise(eCipherError, "authentication tag not supported by this cipher"); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag)) - ossl_raise(eCipherError, "unable to set AEAD tag"); + ossl_raise(eCipherError, "unable to set AEAD tag"); return vtag; } /* * call-seq: - * cipher.auth_tag_len = Integer -> Integer + * cipher.auth_tag_len = integer * - * Sets the length of the authentication tag to be generated or to be given for - * AEAD ciphers that requires it as in input parameter. Note that not all AEAD - * ciphers support this method. + * Sets the length of the expected authentication tag for this Cipher. This + * method is available for some of AEAD ciphers that require the length to be + * set before starting encryption or decryption, such as CCM mode or OCB mode. * - * In OCB mode, the length must be supplied both when encrypting and when - * decrypting, and must be before specifying an IV. + * For CCM mode and OCB mode, the tag length must be set before #iv= is set. + * + * See also the "AEAD Interface" section of the man page EVP_EncryptInit(3). + * This method internally calls EVP_CIPHER_CTX_ctrl() with + * EVP_CTRL_AEAD_SET_TAG and a NULL buffer. */ static VALUE ossl_cipher_set_auth_tag_len(VALUE self, VALUE vlen) @@ -671,10 +715,10 @@ ossl_cipher_set_auth_tag_len(VALUE self, VALUE vlen) GetCipher(self, ctx); if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) - ossl_raise(eCipherError, "AEAD not supported by this cipher"); + ossl_raise(eCipherError, "AEAD not supported by this cipher"); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, NULL)) - ossl_raise(eCipherError, "unable to set authentication tag length"); + ossl_raise(eCipherError, "unable to set authentication tag length"); /* for #auth_tag */ rb_ivar_set(self, id_auth_tag_len, INT2NUM(tag_len)); @@ -684,11 +728,16 @@ ossl_cipher_set_auth_tag_len(VALUE self, VALUE vlen) /* * call-seq: - * cipher.iv_len = integer -> integer + * cipher.iv_len = integer + * + * Sets the IV/nonce length for this Cipher. This method is available for AEAD + * ciphers that support variable IV lengths. This method can be called if a + * different IV length than OpenSSL's default is desired, prior to calling + * #iv=. * - * Sets the IV/nonce length of the Cipher. Normally block ciphers don't allow - * changing the IV length, but some make use of IV for 'nonce'. You may need - * this for interoperability with other applications. + * See also the "AEAD Interface" section of the man page EVP_EncryptInit(3). + * This method internally calls EVP_CIPHER_CTX_ctrl() with + * EVP_CTRL_AEAD_SET_IVLEN. */ static VALUE ossl_cipher_set_iv_length(VALUE self, VALUE iv_length) @@ -698,10 +747,10 @@ ossl_cipher_set_iv_length(VALUE self, VALUE iv_length) GetCipher(self, ctx); if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) - ossl_raise(eCipherError, "cipher does not support AEAD"); + ossl_raise(eCipherError, "cipher does not support AEAD"); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, len, NULL)) - ossl_raise(eCipherError, "unable to set IV length"); + ossl_raise(eCipherError, "unable to set IV length"); /* * EVP_CIPHER_CTX_iv_length() returns the default length. So we need to save @@ -714,13 +763,14 @@ ossl_cipher_set_iv_length(VALUE self, VALUE iv_length) /* * call-seq: - * cipher.key_len = integer -> integer + * cipher.key_len = integer * * Sets the key length of the cipher. If the cipher is a fixed length cipher * then attempting to set the key length to any value other than the fixed * value is an error. * - * Under normal circumstances you do not need to call this method (and probably shouldn't). + * Under normal circumstances you do not need to call this method (and + * probably shouldn't). * * See EVP_CIPHER_CTX_set_key_length for further information. */ @@ -737,13 +787,16 @@ ossl_cipher_set_key_length(VALUE self, VALUE key_length) return key_length; } +// TODO: Should #padding= take a boolean value instead? /* * call-seq: - * cipher.padding = integer -> integer + * cipher.padding = 1 or 0 * - * Enables or disables padding. By default encryption operations are padded using standard block padding and the - * padding is checked and removed when decrypting. If the pad parameter is zero then no padding is performed, the - * total amount of data encrypted or decrypted must then be a multiple of the block size or an error will occur. + * Enables or disables padding. By default encryption operations are padded + * using standard block padding and the padding is checked and removed when + * decrypting. If the pad parameter is zero then no padding is performed, the + * total amount of data encrypted or decrypted must then be a multiple of the + * block size or an error will occur. * * See EVP_CIPHER_CTX_set_padding for further information. */ @@ -755,7 +808,7 @@ ossl_cipher_set_padding(VALUE self, VALUE padding) GetCipher(self, ctx); if (EVP_CIPHER_CTX_set_padding(ctx, pad) != 1) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, NULL); return padding; } @@ -789,9 +842,9 @@ ossl_cipher_iv_length(VALUE self) GetCipher(self, ctx); if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) - len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx); + len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx); if (!len) - len = EVP_CIPHER_CTX_iv_length(ctx); + len = EVP_CIPHER_CTX_iv_length(ctx); return INT2NUM(len); } @@ -814,13 +867,17 @@ ossl_cipher_block_size(VALUE self) /* * call-seq: - * cipher.ccm_data_len = integer -> integer + * cipher.ccm_data_len = integer * - * Sets the length of the plaintext / ciphertext message that will be - * processed in CCM mode. Make sure to call this method after #key= and - * #iv= have been set, and before #auth_data=. + * Sets the total length of the plaintext / ciphertext message that will be + * processed by #update in CCM mode. * - * Only call this method after calling Cipher#encrypt or Cipher#decrypt. + * Make sure to call this method after #key= and #iv= have been set, and + * before #auth_data= or #update are called. + * + * This method is only available for CCM mode ciphers. + * + * See also the "AEAD Interface" section of the man page EVP_EncryptInit(3). */ static VALUE ossl_cipher_set_ccm_data_len(VALUE self, VALUE data_len) @@ -843,11 +900,6 @@ ossl_cipher_set_ccm_data_len(VALUE self, VALUE data_len) void Init_ossl_cipher(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - /* Document-class: OpenSSL::Cipher * * Provides symmetric algorithms for encryption and decryption. The @@ -1003,24 +1055,28 @@ Init_ossl_cipher(void) * could otherwise be exploited to modify ciphertexts in ways beneficial to * potential attackers. * - * An associated data is used where there is additional information, such as + * Associated data, also called additional authenticated data (AAD), is + * optionally used where there is additional information, such as * headers or some metadata, that must be also authenticated but not - * necessarily need to be encrypted. If no associated data is needed for - * encryption and later decryption, the OpenSSL library still requires a - * value to be set - "" may be used in case none is available. + * necessarily need to be encrypted. * * An example using the GCM (Galois/Counter Mode). You have 16 bytes _key_, * 12 bytes (96 bits) _nonce_ and the associated data _auth_data_. Be sure * not to reuse the _key_ and _nonce_ pair. Reusing an nonce ruins the * security guarantees of GCM mode. * + * key = OpenSSL::Random.random_bytes(16) + * nonce = OpenSSL::Random.random_bytes(12) + * auth_data = "authenticated but unencrypted data" + * data = "encrypted data" + * * cipher = OpenSSL::Cipher.new('aes-128-gcm').encrypt * cipher.key = key * cipher.iv = nonce * cipher.auth_data = auth_data * * encrypted = cipher.update(data) + cipher.final - * tag = cipher.auth_tag # produces 16 bytes tag by default + * tag = cipher.auth_tag(16) * * Now you are the receiver. You know the _key_ and have received _nonce_, * _auth_data_, _encrypted_ and _tag_ through an untrusted network. Note @@ -1033,23 +1089,29 @@ Init_ossl_cipher(void) * decipher = OpenSSL::Cipher.new('aes-128-gcm').decrypt * decipher.key = key * decipher.iv = nonce - * decipher.auth_tag = tag + * decipher.auth_tag = tag # could be called at any time before #final * decipher.auth_data = auth_data * * decrypted = decipher.update(encrypted) + decipher.final * * puts data == decrypted #=> true + * + * Note that other AEAD ciphers may require additional steps, such as + * setting the expected tag length (#auth_tag_len=) or the total data + * length (#ccm_data_len=) in advance. Make sure to read the relevant man + * page for details. */ cCipher = rb_define_class_under(mOSSL, "Cipher", rb_cObject); eCipherError = rb_define_class_under(cCipher, "CipherError", eOSSLError); + eAuthTagError = rb_define_class_under(cCipher, "AuthTagError", eCipherError); rb_define_alloc_func(cCipher, ossl_cipher_alloc); rb_define_method(cCipher, "initialize_copy", ossl_cipher_copy, 1); rb_define_module_function(cCipher, "ciphers", ossl_s_ciphers, 0); rb_define_method(cCipher, "initialize", ossl_cipher_initialize, 1); rb_define_method(cCipher, "reset", ossl_cipher_reset, 0); - rb_define_method(cCipher, "encrypt", ossl_cipher_encrypt, -1); - rb_define_method(cCipher, "decrypt", ossl_cipher_decrypt, -1); + rb_define_method(cCipher, "encrypt", ossl_cipher_encrypt, 0); + rb_define_method(cCipher, "decrypt", ossl_cipher_decrypt, 0); rb_define_method(cCipher, "pkcs5_keyivgen", ossl_cipher_pkcs5_keyivgen, -1); rb_define_method(cCipher, "update", ossl_cipher_update, -1); rb_define_method(cCipher, "final", ossl_cipher_final, 0); @@ -1071,4 +1133,5 @@ Init_ossl_cipher(void) id_auth_tag_len = rb_intern_const("auth_tag_len"); id_key_set = rb_intern_const("key_set"); + id_cipher_holder = rb_intern_const("EVP_CIPHER_holder"); } diff --git a/ext/openssl/ossl_cipher.h b/ext/openssl/ossl_cipher.h index 2392d41c6a..fba63a140f 100644 --- a/ext/openssl/ossl_cipher.h +++ b/ext/openssl/ossl_cipher.h @@ -5,15 +5,21 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(_OSSL_CIPHER_H_) #define _OSSL_CIPHER_H_ -extern VALUE cCipher; -extern VALUE eCipherError; - -const EVP_CIPHER *ossl_evp_get_cipherbyname(VALUE); +/* + * Gets EVP_CIPHER from a String or an OpenSSL::Digest instance (discouraged, + * but still supported for compatibility). A holder object is created if the + * EVP_CIPHER is a "fetched" algorithm. + */ +const EVP_CIPHER *ossl_evp_cipher_fetch(VALUE obj, volatile VALUE *holder); +/* + * This is meant for OpenSSL::Engine#cipher. EVP_CIPHER must not be a fetched + * one. + */ VALUE ossl_cipher_new(const EVP_CIPHER *); void Init_ossl_cipher(void); diff --git a/ext/openssl/ossl_config.c b/ext/openssl/ossl_config.c index 0e598b4d51..274875a978 100644 --- a/ext/openssl/ossl_config.c +++ b/ext/openssl/ossl_config.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -22,7 +22,7 @@ static const rb_data_type_t ossl_config_type = { { 0, nconf_free, }, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE, }; CONF * @@ -87,6 +87,7 @@ config_s_parse(VALUE klass, VALUE str) bio = ossl_obj2bio(&str); config_load_bio(conf, bio); /* Consumes BIO */ + rb_obj_freeze(obj); return obj; } @@ -144,6 +145,7 @@ config_initialize(int argc, VALUE *argv, VALUE self) ossl_raise(eConfigError, "BIO_new_file"); config_load_bio(conf, bio); /* Consumes BIO */ } + rb_obj_freeze(self); return self; } @@ -158,6 +160,7 @@ config_initialize_copy(VALUE self, VALUE other) rb_check_frozen(self); bio = ossl_obj2bio(&str); config_load_bio(conf, bio); /* Consumes BIO */ + rb_obj_freeze(self); return self; } @@ -305,18 +308,16 @@ static IMPLEMENT_LHASH_DOALL_ARG_FN(dump_conf_value, CONF_VALUE, VALUE) * * Gets the parsable form of the current configuration. * - * Given the following configuration being created: + * Given the following configuration file being loaded: * - * config = OpenSSL::Config.new - * #=> #<OpenSSL::Config sections=[]> - * config['default'] = {"foo"=>"bar","baz"=>"buz"} - * #=> {"foo"=>"bar", "baz"=>"buz"} + * config = OpenSSL::Config.load('baz.cnf') + * #=> #<OpenSSL::Config sections=["default"]> * puts config.to_s * #=> [ default ] * # foo=bar * # baz=buz * - * You can parse get the serialized configuration using #to_s and then parse + * You can get the serialized configuration using #to_s and then parse * it later: * * serialized_config = config.to_s @@ -412,11 +413,6 @@ Init_ossl_config(void) char *path; VALUE path_str; -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - /* Document-class: OpenSSL::Config * * Configuration for the openssl library. @@ -425,7 +421,7 @@ Init_ossl_config(void) * configuration. See the value of OpenSSL::Config::DEFAULT_CONFIG_FILE for * the location of the file for your host. * - * See also http://www.openssl.org/docs/apps/config.html + * See also https://docs.openssl.org/master/man5/config/ */ cConfig = rb_define_class_under(mOSSL, "Config", rb_cObject); @@ -455,6 +451,6 @@ Init_ossl_config(void) * The default system configuration file for OpenSSL. */ path = CONF_get1_default_config_file(); - path_str = ossl_buf2str(path, rb_long2int(strlen(path))); + path_str = rb_obj_freeze(ossl_buf2str(path, rb_long2int(strlen(path)))); rb_define_const(cConfig, "DEFAULT_CONFIG_FILE", path_str); } diff --git a/ext/openssl/ossl_config.h b/ext/openssl/ossl_config.h index 4e604f1aed..a254360c2c 100644 --- a/ext/openssl/ossl_config.h +++ b/ext/openssl/ossl_config.h @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #ifndef OSSL_CONFIG_H #define OSSL_CONFIG_H diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c index 16aeeb8106..e23968b1e3 100644 --- a/ext/openssl/ossl_digest.c +++ b/ext/openssl/ossl_digest.c @@ -5,22 +5,23 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" #define GetDigest(obj, ctx) do { \ TypedData_Get_Struct((obj), EVP_MD_CTX, &ossl_digest_type, (ctx)); \ if (!(ctx)) { \ - ossl_raise(rb_eRuntimeError, "Digest CTX wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "Digest CTX wasn't initialized!"); \ } \ } while (0) /* * Classes */ -VALUE cDigest; -VALUE eDigestError; +static VALUE cDigest; +static VALUE eDigestError; +static ID id_md_holder; static VALUE ossl_digest_alloc(VALUE klass); @@ -33,39 +34,67 @@ ossl_digest_free(void *ctx) static const rb_data_type_t ossl_digest_type = { "OpenSSL/Digest", { - 0, ossl_digest_free, + 0, ossl_digest_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; +#ifdef OSSL_USE_PROVIDER +static void +ossl_evp_md_free(void *ptr) +{ + // This is safe to call against const EVP_MD * returned by + // EVP_get_digestbyname() + EVP_MD_free(ptr); +} + +static const rb_data_type_t ossl_evp_md_holder_type = { + "OpenSSL/EVP_MD", + { + .dfree = ossl_evp_md_free, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, +}; +#endif + /* * Public */ const EVP_MD * -ossl_evp_get_digestbyname(VALUE obj) +ossl_evp_md_fetch(VALUE obj, volatile VALUE *holder) { - const EVP_MD *md; - ASN1_OBJECT *oid = NULL; - - if (RB_TYPE_P(obj, T_STRING)) { - const char *name = StringValueCStr(obj); - - md = EVP_get_digestbyname(name); - if (!md) { - oid = OBJ_txt2obj(name, 0); - md = EVP_get_digestbyobj(oid); - ASN1_OBJECT_free(oid); - } - if(!md) - ossl_raise(rb_eRuntimeError, "Unsupported digest algorithm (%"PRIsVALUE").", obj); - } else { + *holder = Qnil; + if (rb_obj_is_kind_of(obj, cDigest)) { EVP_MD_CTX *ctx; - GetDigest(obj, ctx); - - md = EVP_MD_CTX_get0_md(ctx); + EVP_MD *md = (EVP_MD *)EVP_MD_CTX_get0_md(ctx); +#ifdef OSSL_USE_PROVIDER + *holder = TypedData_Wrap_Struct(0, &ossl_evp_md_holder_type, NULL); + if (!EVP_MD_up_ref(md)) + ossl_raise(eDigestError, "EVP_MD_up_ref"); + RTYPEDDATA_DATA(*holder) = md; +#endif + return md; } + const char *name = StringValueCStr(obj); + EVP_MD *md = (EVP_MD *)EVP_get_digestbyname(name); + if (!md) { + ASN1_OBJECT *oid = OBJ_txt2obj(name, 0); + md = (EVP_MD *)EVP_get_digestbyobj(oid); + ASN1_OBJECT_free(oid); + } +#ifdef OSSL_USE_PROVIDER + if (!md) { + ossl_clear_error(); + *holder = TypedData_Wrap_Struct(0, &ossl_evp_md_holder_type, NULL); + md = EVP_MD_fetch(NULL, name, NULL); + RTYPEDDATA_DATA(*holder) = md; + } +#endif + if (!md) + ossl_raise(eDigestError, "unsupported digest algorithm: %"PRIsVALUE, + obj); return md; } @@ -75,14 +104,17 @@ ossl_digest_new(const EVP_MD *md) VALUE ret; EVP_MD_CTX *ctx; + // NOTE: This does not set id_md_holder because this function should + // only be called from ossl_engine.c, which will not use any + // reference-counted digests. ret = ossl_digest_alloc(cDigest); ctx = EVP_MD_CTX_new(); if (!ctx) - ossl_raise(eDigestError, "EVP_MD_CTX_new"); + ossl_raise(eDigestError, "EVP_MD_CTX_new"); RTYPEDDATA_DATA(ret) = ctx; if (!EVP_DigestInit_ex(ctx, md, NULL)) - ossl_raise(eDigestError, "Digest initialization failed"); + ossl_raise(eDigestError, "Digest initialization failed"); return ret; } @@ -96,14 +128,15 @@ ossl_digest_alloc(VALUE klass) return TypedData_Wrap_Struct(klass, &ossl_digest_type, 0); } -VALUE ossl_digest_update(VALUE, VALUE); +static VALUE ossl_digest_update(VALUE, VALUE); /* * call-seq: * Digest.new(string [, data]) -> Digest * * Creates a Digest instance based on _string_, which is either the ln - * (long name) or sn (short name) of a supported digest algorithm. + * (long name) or sn (short name) of a supported digest algorithm. A list of + * supported algorithms can be obtained by calling OpenSSL::Digest.digests. * * If _data_ (a String) is given, it is used as the initial input to the * Digest instance, i.e. @@ -120,26 +153,28 @@ ossl_digest_initialize(int argc, VALUE *argv, VALUE self) { EVP_MD_CTX *ctx; const EVP_MD *md; - VALUE type, data; + VALUE type, data, md_holder; rb_scan_args(argc, argv, "11", &type, &data); - md = ossl_evp_get_digestbyname(type); + md = ossl_evp_md_fetch(type, &md_holder); if (!NIL_P(data)) StringValue(data); TypedData_Get_Struct(self, EVP_MD_CTX, &ossl_digest_type, ctx); if (!ctx) { - RTYPEDDATA_DATA(self) = ctx = EVP_MD_CTX_new(); - if (!ctx) - ossl_raise(eDigestError, "EVP_MD_CTX_new"); + RTYPEDDATA_DATA(self) = ctx = EVP_MD_CTX_new(); + if (!ctx) + ossl_raise(eDigestError, "EVP_MD_CTX_new"); } if (!EVP_DigestInit_ex(ctx, md, NULL)) - ossl_raise(eDigestError, "Digest initialization failed"); + ossl_raise(eDigestError, "Digest initialization failed"); + rb_ivar_set(self, id_md_holder, md_holder); if (!NIL_P(data)) return ossl_digest_update(self, data); return self; } +/* :nodoc: */ static VALUE ossl_digest_copy(VALUE self, VALUE other) { @@ -150,18 +185,44 @@ ossl_digest_copy(VALUE self, VALUE other) TypedData_Get_Struct(self, EVP_MD_CTX, &ossl_digest_type, ctx1); if (!ctx1) { - RTYPEDDATA_DATA(self) = ctx1 = EVP_MD_CTX_new(); - if (!ctx1) - ossl_raise(eDigestError, "EVP_MD_CTX_new"); + RTYPEDDATA_DATA(self) = ctx1 = EVP_MD_CTX_new(); + if (!ctx1) + ossl_raise(eDigestError, "EVP_MD_CTX_new"); } GetDigest(other, ctx2); if (!EVP_MD_CTX_copy(ctx1, ctx2)) { - ossl_raise(eDigestError, NULL); + ossl_raise(eDigestError, NULL); } return self; } +static void +add_digest_name_to_ary(const OBJ_NAME *name, void *arg) +{ + VALUE ary = (VALUE)arg; + rb_ary_push(ary, rb_str_new2(name->name)); +} + +/* + * call-seq: + * OpenSSL::Digest.digests -> array[string...] + * + * Returns the names of all available digests in an array. + */ +static VALUE +ossl_s_digests(VALUE self) +{ + VALUE ary; + + ary = rb_ary_new(); + OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH, + add_digest_name_to_ary, + (void*)ary); + + return ary; +} + /* * call-seq: * digest.reset -> self @@ -177,7 +238,7 @@ ossl_digest_reset(VALUE self) GetDigest(self, ctx); if (EVP_DigestInit_ex(ctx, EVP_MD_CTX_get0_md(ctx), NULL) != 1) { - ossl_raise(eDigestError, "Digest initialization failed."); + ossl_raise(eDigestError, "Digest initialization failed."); } return self; @@ -198,7 +259,7 @@ ossl_digest_reset(VALUE self) * result = digest.digest * */ -VALUE +static VALUE ossl_digest_update(VALUE self, VALUE data) { EVP_MD_CTX *ctx; @@ -207,7 +268,7 @@ ossl_digest_update(VALUE self, VALUE data) GetDigest(self, ctx); if (!EVP_DigestUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) - ossl_raise(eDigestError, "EVP_DigestUpdate"); + ossl_raise(eDigestError, "EVP_DigestUpdate"); return self; } @@ -218,25 +279,15 @@ ossl_digest_update(VALUE self, VALUE data) * */ static VALUE -ossl_digest_finish(int argc, VALUE *argv, VALUE self) +ossl_digest_finish(VALUE self) { EVP_MD_CTX *ctx; VALUE str; - int out_len; GetDigest(self, ctx); - rb_scan_args(argc, argv, "01", &str); - out_len = EVP_MD_CTX_size(ctx); - - if (NIL_P(str)) { - str = rb_str_new(NULL, out_len); - } else { - StringValue(str); - rb_str_resize(str, out_len); - } - + str = rb_str_new(NULL, EVP_MD_CTX_size(ctx)); if (!EVP_DigestFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), NULL)) - ossl_raise(eDigestError, "EVP_DigestFinal_ex"); + ossl_raise(eDigestError, "EVP_DigestFinal_ex"); return str; } @@ -245,7 +296,8 @@ ossl_digest_finish(int argc, VALUE *argv, VALUE self) * call-seq: * digest.name -> string * - * Returns the sn of this Digest algorithm. + * Returns the short name of this Digest algorithm which may differ slightly + * from the original name provided. * * === Example * digest = OpenSSL::Digest.new('SHA512') @@ -313,11 +365,6 @@ ossl_digest_block_length(VALUE self) void Init_ossl_digest(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - /* Document-class: OpenSSL::Digest * * OpenSSL::Digest allows you to compute message digests (sometimes @@ -412,14 +459,17 @@ Init_ossl_digest(void) rb_define_alloc_func(cDigest, ossl_digest_alloc); + rb_define_module_function(cDigest, "digests", ossl_s_digests, 0); rb_define_method(cDigest, "initialize", ossl_digest_initialize, -1); rb_define_method(cDigest, "initialize_copy", ossl_digest_copy, 1); rb_define_method(cDigest, "reset", ossl_digest_reset, 0); rb_define_method(cDigest, "update", ossl_digest_update, 1); rb_define_alias(cDigest, "<<", "update"); - rb_define_private_method(cDigest, "finish", ossl_digest_finish, -1); + rb_define_private_method(cDigest, "finish", ossl_digest_finish, 0); rb_define_method(cDigest, "digest_length", ossl_digest_size, 0); rb_define_method(cDigest, "block_length", ossl_digest_block_length, 0); rb_define_method(cDigest, "name", ossl_digest_name, 0); + + id_md_holder = rb_intern_const("EVP_MD_holder"); } diff --git a/ext/openssl/ossl_digest.h b/ext/openssl/ossl_digest.h index 50bf5666a3..9c3bb2b149 100644 --- a/ext/openssl/ossl_digest.h +++ b/ext/openssl/ossl_digest.h @@ -5,15 +5,20 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(_OSSL_DIGEST_H_) #define _OSSL_DIGEST_H_ -extern VALUE cDigest; -extern VALUE eDigestError; - -const EVP_MD *ossl_evp_get_digestbyname(VALUE); +/* + * Gets EVP_MD from a String or an OpenSSL::Digest instance (discouraged, but + * still supported for compatibility). A holder object is created if the EVP_MD + * is a "fetched" algorithm. + */ +const EVP_MD *ossl_evp_md_fetch(VALUE obj, volatile VALUE *holder); +/* + * This is meant for OpenSSL::Engine#digest. EVP_MD must not be a fetched one. + */ VALUE ossl_digest_new(const EVP_MD *); void Init_ossl_digest(void); diff --git a/ext/openssl/ossl_engine.c b/ext/openssl/ossl_engine.c index 9e86321d06..a2bcb07ea4 100644 --- a/ext/openssl/ossl_engine.c +++ b/ext/openssl/ossl_engine.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -16,7 +16,7 @@ TypedData_Wrap_Struct((klass), &ossl_engine_type, 0) #define SetEngine(obj, engine) do { \ if (!(engine)) { \ - ossl_raise(rb_eRuntimeError, "ENGINE wasn't initialized."); \ + ossl_raise(rb_eRuntimeError, "ENGINE wasn't initialized."); \ } \ RTYPEDDATA_DATA(obj) = (engine); \ } while(0) @@ -37,35 +37,25 @@ * * See also, https://www.openssl.org/docs/crypto/engine.html */ -VALUE cEngine; +static VALUE cEngine; /* Document-class: OpenSSL::Engine::EngineError * * This is the generic exception for OpenSSL::Engine related errors */ -VALUE eEngineError; +static VALUE eEngineError; /* * Private */ -#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000 #define OSSL_ENGINE_LOAD_IF_MATCH(engine_name, x) \ do{\ - if(!strcmp(#engine_name, RSTRING_PTR(name))){\ - if (OPENSSL_init_crypto(OPENSSL_INIT_ENGINE_##x, NULL))\ - return Qtrue;\ - else\ - ossl_raise(eEngineError, "OPENSSL_init_crypto"); \ - }\ -}while(0) -#else -#define OSSL_ENGINE_LOAD_IF_MATCH(engine_name, x) \ -do{\ - if(!strcmp(#engine_name, RSTRING_PTR(name))){\ - ENGINE_load_##engine_name();\ - return Qtrue;\ - }\ + if(!strcmp(#engine_name, RSTRING_PTR(name))){\ + if (OPENSSL_init_crypto(OPENSSL_INIT_ENGINE_##x, NULL))\ + return Qtrue;\ + else\ + ossl_raise(eEngineError, "OPENSSL_init_crypto"); \ + }\ }while(0) -#endif static void ossl_engine_free(void *engine) @@ -76,7 +66,7 @@ ossl_engine_free(void *engine) static const rb_data_type_t ossl_engine_type = { "OpenSSL/Engine", { - 0, ossl_engine_free, + 0, ossl_engine_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -102,50 +92,10 @@ ossl_engine_s_load(int argc, VALUE *argv, VALUE klass) return Qtrue; } StringValueCStr(name); -#ifdef HAVE_ENGINE_LOAD_DYNAMIC OSSL_ENGINE_LOAD_IF_MATCH(dynamic, DYNAMIC); -#endif -#ifndef OPENSSL_NO_STATIC_ENGINE -#ifdef HAVE_ENGINE_LOAD_4758CCA - OSSL_ENGINE_LOAD_IF_MATCH(4758cca, 4758CCA); -#endif -#ifdef HAVE_ENGINE_LOAD_AEP - OSSL_ENGINE_LOAD_IF_MATCH(aep, AEP); -#endif -#ifdef HAVE_ENGINE_LOAD_ATALLA - OSSL_ENGINE_LOAD_IF_MATCH(atalla, ATALLA); -#endif -#ifdef HAVE_ENGINE_LOAD_CHIL - OSSL_ENGINE_LOAD_IF_MATCH(chil, CHIL); -#endif -#ifdef HAVE_ENGINE_LOAD_CSWIFT - OSSL_ENGINE_LOAD_IF_MATCH(cswift, CSWIFT); -#endif -#ifdef HAVE_ENGINE_LOAD_NURON - OSSL_ENGINE_LOAD_IF_MATCH(nuron, NURON); -#endif -#ifdef HAVE_ENGINE_LOAD_SUREWARE - OSSL_ENGINE_LOAD_IF_MATCH(sureware, SUREWARE); -#endif -#ifdef HAVE_ENGINE_LOAD_UBSEC - OSSL_ENGINE_LOAD_IF_MATCH(ubsec, UBSEC); -#endif -#ifdef HAVE_ENGINE_LOAD_PADLOCK OSSL_ENGINE_LOAD_IF_MATCH(padlock, PADLOCK); -#endif -#ifdef HAVE_ENGINE_LOAD_CAPI OSSL_ENGINE_LOAD_IF_MATCH(capi, CAPI); -#endif -#ifdef HAVE_ENGINE_LOAD_GMP - OSSL_ENGINE_LOAD_IF_MATCH(gmp, GMP); -#endif -#ifdef HAVE_ENGINE_LOAD_GOST - OSSL_ENGINE_LOAD_IF_MATCH(gost, GOST); -#endif -#endif -#ifdef HAVE_ENGINE_LOAD_CRYPTODEV OSSL_ENGINE_LOAD_IF_MATCH(cryptodev, CRYPTODEV); -#endif OSSL_ENGINE_LOAD_IF_MATCH(openssl, OPENSSL); rb_warning("no such builtin loader for `%"PRIsVALUE"'", name); return Qnil; @@ -163,9 +113,6 @@ ossl_engine_s_load(int argc, VALUE *argv, VALUE klass) static VALUE ossl_engine_s_cleanup(VALUE self) { -#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000 - ENGINE_cleanup(); -#endif return Qnil; } @@ -183,12 +130,12 @@ ossl_engine_s_engines(VALUE klass) ary = rb_ary_new(); for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)){ - obj = NewEngine(klass); - /* Need a ref count of two here because of ENGINE_free being - * called internally by OpenSSL when moving to the next ENGINE - * and by us when releasing the ENGINE reference */ - ENGINE_up_ref(e); - SetEngine(obj, e); + obj = NewEngine(klass); + /* Need a ref count of two here because of ENGINE_free being + * called internally by OpenSSL when moving to the next ENGINE + * and by us when releasing the ENGINE reference */ + ENGINE_up_ref(e); + SetEngine(obj, e); rb_ary_push(ary, obj); } @@ -216,13 +163,13 @@ ossl_engine_s_by_id(VALUE klass, VALUE id) ossl_engine_s_load(1, &id, klass); obj = NewEngine(klass); if(!(e = ENGINE_by_id(RSTRING_PTR(id)))) - ossl_raise(eEngineError, NULL); + ossl_raise(eEngineError, NULL); SetEngine(obj, e); if(rb_block_given_p()) rb_yield(obj); if(!ENGINE_init(e)) - ossl_raise(eEngineError, NULL); + ossl_raise(eEngineError, NULL); ENGINE_ctrl(e, ENGINE_CTRL_SET_PASSWORD_CALLBACK, - 0, NULL, (void(*)(void))ossl_pem_passwd_cb); + 0, NULL, (void(*)(void))ossl_pem_passwd_cb); ossl_clear_error(); return obj; @@ -237,7 +184,7 @@ ossl_engine_s_by_id(VALUE klass, VALUE id) * OpenSSL::Engine.load * OpenSSL::Engine.engines #=> [#<OpenSSL::Engine#>, ...] * OpenSSL::Engine.engines.first.id - * #=> "rsax" + * #=> "rsax" */ static VALUE ossl_engine_get_id(VALUE self) @@ -256,7 +203,7 @@ ossl_engine_get_id(VALUE self) * OpenSSL::Engine.load * OpenSSL::Engine.engines #=> [#<OpenSSL::Engine#>, ...] * OpenSSL::Engine.engines.first.name - * #=> "RSAX engine support" + * #=> "RSAX engine support" * */ static VALUE @@ -327,11 +274,11 @@ ossl_engine_get_cipher(VALUE self, VALUE name) * Will raise an EngineError if the digest is unavailable. * * e = OpenSSL::Engine.by_id("openssl") - * #=> #<OpenSSL::Engine id="openssl" name="Software engine support"> + * #=> #<OpenSSL::Engine id="openssl" name="Software engine support"> * e.digest("SHA1") - * #=> #<OpenSSL::Digest: da39a3ee5e6b4b0d3255bfef95601890afd80709> + * #=> #<OpenSSL::Digest: da39a3ee5e6b4b0d3255bfef95601890afd80709> * e.digest("zomg") - * #=> OpenSSL::Engine::EngineError: no such digest `zomg' + * #=> OpenSSL::Engine::EngineError: no such digest `zomg' */ static VALUE ossl_engine_get_digest(VALUE self, VALUE name) @@ -373,7 +320,7 @@ ossl_engine_load_privkey(int argc, VALUE *argv, VALUE self) GetEngine(self, e); pkey = ENGINE_load_private_key(e, sid, NULL, sdata); if (!pkey) ossl_raise(eEngineError, NULL); - obj = ossl_pkey_new(pkey); + obj = ossl_pkey_wrap(pkey); OSSL_PKEY_SET_PRIVATE(obj); return obj; @@ -403,7 +350,7 @@ ossl_engine_load_pubkey(int argc, VALUE *argv, VALUE self) pkey = ENGINE_load_public_key(e, sid, NULL, sdata); if (!pkey) ossl_raise(eEngineError, NULL); - return ossl_pkey_new(pkey); + return ossl_pkey_wrap(pkey); } /* @@ -418,7 +365,7 @@ ossl_engine_load_pubkey(int argc, VALUE *argv, VALUE self) * your OS. * * [All flags] 0xFFFF - * [No flags] 0x0000 + * [No flags] 0x0000 * * See also <openssl/engine.h> */ @@ -452,7 +399,7 @@ ossl_engine_ctrl_cmd(int argc, VALUE *argv, VALUE self) GetEngine(self, e); rb_scan_args(argc, argv, "11", &cmd, &val); ret = ENGINE_ctrl_cmd_string(e, StringValueCStr(cmd), - NIL_P(val) ? NULL : StringValueCStr(val), 0); + NIL_P(val) ? NULL : StringValueCStr(val), 0); if (!ret) ossl_raise(eEngineError, NULL); return self; @@ -462,11 +409,11 @@ static VALUE ossl_engine_cmd_flag_to_name(int flag) { switch(flag){ - case ENGINE_CMD_FLAG_NUMERIC: return rb_str_new2("NUMERIC"); - case ENGINE_CMD_FLAG_STRING: return rb_str_new2("STRING"); - case ENGINE_CMD_FLAG_NO_INPUT: return rb_str_new2("NO_INPUT"); - case ENGINE_CMD_FLAG_INTERNAL: return rb_str_new2("INTERNAL"); - default: return rb_str_new2("UNKNOWN"); + case ENGINE_CMD_FLAG_NUMERIC: return rb_str_new2("NUMERIC"); + case ENGINE_CMD_FLAG_STRING: return rb_str_new2("STRING"); + case ENGINE_CMD_FLAG_NO_INPUT: return rb_str_new2("NO_INPUT"); + case ENGINE_CMD_FLAG_INTERNAL: return rb_str_new2("INTERNAL"); + default: return rb_str_new2("UNKNOWN"); } } @@ -486,13 +433,13 @@ ossl_engine_get_cmds(VALUE self) GetEngine(self, e); ary = rb_ary_new(); if ((defn = ENGINE_get_cmd_defns(e)) != NULL){ - for (p = defn; p->cmd_num > 0; p++){ - tmp = rb_ary_new(); - rb_ary_push(tmp, rb_str_new2(p->cmd_name)); - rb_ary_push(tmp, rb_str_new2(p->cmd_desc)); - rb_ary_push(tmp, ossl_engine_cmd_flag_to_name(p->cmd_flags)); - rb_ary_push(ary, tmp); - } + for (p = defn; p->cmd_num > 0; p++){ + tmp = rb_ary_new(); + rb_ary_push(tmp, rb_str_new2(p->cmd_name)); + rb_ary_push(tmp, rb_str_new2(p->cmd_desc)); + rb_ary_push(tmp, ossl_engine_cmd_flag_to_name(p->cmd_flags)); + rb_ary_push(ary, tmp); + } } return ary; @@ -511,7 +458,7 @@ ossl_engine_inspect(VALUE self) GetEngine(self, e); return rb_sprintf("#<%"PRIsVALUE" id=\"%s\" name=\"%s\">", - rb_obj_class(self), ENGINE_get_id(e), ENGINE_get_name(e)); + rb_obj_class(self), ENGINE_get_id(e), ENGINE_get_name(e)); } #define DefEngineConst(x) rb_define_const(cEngine, #x, INT2NUM(ENGINE_##x)) @@ -519,11 +466,6 @@ ossl_engine_inspect(VALUE self) void Init_ossl_engine(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - cEngine = rb_define_class_under(mOSSL, "Engine", rb_cObject); eEngineError = rb_define_class_under(cEngine, "EngineError", eOSSLError); @@ -549,12 +491,6 @@ Init_ossl_engine(void) DefEngineConst(METHOD_DSA); DefEngineConst(METHOD_DH); DefEngineConst(METHOD_RAND); -#ifdef ENGINE_METHOD_BN_MOD_EXP - DefEngineConst(METHOD_BN_MOD_EXP); -#endif -#ifdef ENGINE_METHOD_BN_MOD_EXP_CRT - DefEngineConst(METHOD_BN_MOD_EXP_CRT); -#endif DefEngineConst(METHOD_CIPHERS); DefEngineConst(METHOD_DIGESTS); DefEngineConst(METHOD_ALL); diff --git a/ext/openssl/ossl_engine.h b/ext/openssl/ossl_engine.h index cd548beea3..cedcebb772 100644 --- a/ext/openssl/ossl_engine.h +++ b/ext/openssl/ossl_engine.h @@ -6,14 +6,11 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(OSSL_ENGINE_H) #define OSSL_ENGINE_H -extern VALUE cEngine; -extern VALUE eEngineError; - void Init_ossl_engine(void); #endif /* OSSL_ENGINE_H */ diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c index c485ba7e67..ddcc6a5f8d 100644 --- a/ext/openssl/ossl_hmac.c +++ b/ext/openssl/ossl_hmac.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -14,15 +14,16 @@ #define GetHMAC(obj, ctx) do { \ TypedData_Get_Struct((obj), EVP_MD_CTX, &ossl_hmac_type, (ctx)); \ if (!(ctx)) { \ - ossl_raise(rb_eRuntimeError, "HMAC wasn't initialized"); \ + ossl_raise(rb_eRuntimeError, "HMAC wasn't initialized"); \ } \ } while (0) /* * Classes */ -VALUE cHMAC; -VALUE eHMACError; +static VALUE cHMAC; +static VALUE eHMACError; +static ID id_md_holder; /* * Public @@ -40,7 +41,7 @@ ossl_hmac_free(void *ctx) static const rb_data_type_t ossl_hmac_type = { "OpenSSL/HMAC", { - 0, ossl_hmac_free, + 0, ossl_hmac_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -73,17 +74,17 @@ ossl_hmac_alloc(VALUE klass) * * === Example * - * key = 'key' - * instance = OpenSSL::HMAC.new(key, 'SHA1') - * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f - * instance.class - * #=> OpenSSL::HMAC + * key = 'key' + * instance = OpenSSL::HMAC.new(key, 'SHA1') + * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f + * instance.class + * #=> OpenSSL::HMAC * * === A note about comparisons * * Two instances can be securely compared with #== in constant time: * - * other_instance = OpenSSL::HMAC.new('key', 'SHA1') + * other_instance = OpenSSL::HMAC.new('key', 'SHA1') * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * instance == other_instance * #=> true @@ -94,33 +95,29 @@ ossl_hmac_initialize(VALUE self, VALUE key, VALUE digest) { EVP_MD_CTX *ctx; EVP_PKEY *pkey; + const EVP_MD *md; + VALUE md_holder; GetHMAC(self, ctx); StringValue(key); -#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY + md = ossl_evp_md_fetch(digest, &md_holder); pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL, (unsigned char *)RSTRING_PTR(key), RSTRING_LENINT(key)); if (!pkey) ossl_raise(eHMACError, "EVP_PKEY_new_raw_private_key"); -#else - pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, - (unsigned char *)RSTRING_PTR(key), - RSTRING_LENINT(key)); - if (!pkey) - ossl_raise(eHMACError, "EVP_PKEY_new_mac_key"); -#endif - if (EVP_DigestSignInit(ctx, NULL, ossl_evp_get_digestbyname(digest), - NULL, pkey) != 1) { + if (EVP_DigestSignInit(ctx, NULL, md, NULL, pkey) != 1) { EVP_PKEY_free(pkey); ossl_raise(eHMACError, "EVP_DigestSignInit"); } + rb_ivar_set(self, id_md_holder, md_holder); /* Decrement reference counter; EVP_MD_CTX still keeps it */ EVP_PKEY_free(pkey); return self; } +/* :nodoc: */ static VALUE ossl_hmac_copy(VALUE self, VALUE other) { @@ -145,13 +142,13 @@ ossl_hmac_copy(VALUE self, VALUE other) * * === Example * - * first_chunk = 'The quick brown fox jumps ' - * second_chunk = 'over the lazy dog' + * first_chunk = 'The quick brown fox jumps ' + * second_chunk = 'over the lazy dog' * - * instance.update(first_chunk) - * #=> 5b9a8038a65d571076d97fe783989e52278a492a - * instance.update(second_chunk) - * #=> de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9 + * instance.update(first_chunk) + * #=> 5b9a8038a65d571076d97fe783989e52278a492a + * instance.update(second_chunk) + * #=> de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9 * */ static VALUE @@ -229,14 +226,14 @@ ossl_hmac_hexdigest(VALUE self) * * === Example * - * data = "The quick brown fox jumps over the lazy dog" - * instance = OpenSSL::HMAC.new('key', 'SHA1') - * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f + * data = "The quick brown fox jumps over the lazy dog" + * instance = OpenSSL::HMAC.new('key', 'SHA1') + * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * - * instance.update(data) - * #=> de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9 - * instance.reset - * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f + * instance.update(data) + * #=> de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9 + * instance.reset + * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * */ static VALUE @@ -259,11 +256,6 @@ ossl_hmac_reset(VALUE self) void Init_ossl_hmac(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - /* * Document-class: OpenSSL::HMAC * @@ -307,4 +299,6 @@ Init_ossl_hmac(void) rb_define_method(cHMAC, "hexdigest", ossl_hmac_hexdigest, 0); rb_define_alias(cHMAC, "inspect", "hexdigest"); rb_define_alias(cHMAC, "to_s", "hexdigest"); + + id_md_holder = rb_intern_const("EVP_MD_holder"); } diff --git a/ext/openssl/ossl_hmac.h b/ext/openssl/ossl_hmac.h index 7c51f4722d..e5bed37c9f 100644 --- a/ext/openssl/ossl_hmac.h +++ b/ext/openssl/ossl_hmac.h @@ -5,14 +5,11 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(_OSSL_HMAC_H_) #define _OSSL_HMAC_H_ -extern VALUE cHMAC; -extern VALUE eHMACError; - void Init_ossl_hmac(void); #endif /* _OSSL_HMAC_H_ */ diff --git a/ext/openssl/ossl_kdf.c b/ext/openssl/ossl_kdf.c index 48b161d4f4..1f28016440 100644 --- a/ext/openssl/ossl_kdf.c +++ b/ext/openssl/ossl_kdf.c @@ -3,9 +3,7 @@ * Copyright (C) 2007, 2017 Ruby/OpenSSL Project Authors */ #include "ossl.h" -#if OSSL_OPENSSL_PREREQ(1, 1, 0) || OSSL_LIBRESSL_PREREQ(3, 6, 0) -# include <openssl/kdf.h> -#endif +#include <openssl/kdf.h> static VALUE mKDF, eKDF; @@ -18,7 +16,7 @@ static VALUE mKDF, eKDF; * of _length_ bytes. * * For more information about PBKDF2, see RFC 2898 Section 5.2 - * (https://tools.ietf.org/html/rfc2898#section-5.2). + * (https://www.rfc-editor.org/rfc/rfc2898#section-5.2). * * === Parameters * pass :: The password. @@ -37,16 +35,16 @@ static VALUE mKDF, eKDF; static VALUE kdf_pbkdf2_hmac(int argc, VALUE *argv, VALUE self) { - VALUE pass, salt, opts, kwargs[4], str; + VALUE pass, salt, opts, kwargs[4], str, md_holder; static ID kwargs_ids[4]; int iters, len; const EVP_MD *md; if (!kwargs_ids[0]) { - kwargs_ids[0] = rb_intern_const("salt"); - kwargs_ids[1] = rb_intern_const("iterations"); - kwargs_ids[2] = rb_intern_const("length"); - kwargs_ids[3] = rb_intern_const("hash"); + kwargs_ids[0] = rb_intern_const("salt"); + kwargs_ids[1] = rb_intern_const("iterations"); + kwargs_ids[2] = rb_intern_const("length"); + kwargs_ids[3] = rb_intern_const("hash"); } rb_scan_args(argc, argv, "1:", &pass, &opts); rb_get_kwargs(opts, kwargs_ids, 4, 0, kwargs); @@ -55,14 +53,14 @@ kdf_pbkdf2_hmac(int argc, VALUE *argv, VALUE self) salt = StringValue(kwargs[0]); iters = NUM2INT(kwargs[1]); len = NUM2INT(kwargs[2]); - md = ossl_evp_get_digestbyname(kwargs[3]); + md = ossl_evp_md_fetch(kwargs[3], &md_holder); str = rb_str_new(0, len); if (!PKCS5_PBKDF2_HMAC(RSTRING_PTR(pass), RSTRING_LENINT(pass), - (unsigned char *)RSTRING_PTR(salt), - RSTRING_LENINT(salt), iters, md, len, - (unsigned char *)RSTRING_PTR(str))) - ossl_raise(eKDF, "PKCS5_PBKDF2_HMAC"); + (unsigned char *)RSTRING_PTR(salt), + RSTRING_LENINT(salt), iters, md, len, + (unsigned char *)RSTRING_PTR(str))) + ossl_raise(eKDF, "PKCS5_PBKDF2_HMAC"); return str; } @@ -81,10 +79,10 @@ kdf_pbkdf2_hmac(int argc, VALUE *argv, VALUE self) * bcrypt. * * The keyword arguments _N_, _r_ and _p_ can be used to tune scrypt. RFC 7914 - * (published on 2016-08, https://tools.ietf.org/html/rfc7914#section-2) states + * (published on 2016-08, https://www.rfc-editor.org/rfc/rfc7914#section-2) states * that using values r=8 and p=1 appears to yield good results. * - * See RFC 7914 (https://tools.ietf.org/html/rfc7914) for more information. + * See RFC 7914 (https://www.rfc-editor.org/rfc/rfc7914) for more information. * * === Parameters * pass :: Passphrase. @@ -109,11 +107,11 @@ kdf_scrypt(int argc, VALUE *argv, VALUE self) uint64_t N, r, p, maxmem; if (!kwargs_ids[0]) { - kwargs_ids[0] = rb_intern_const("salt"); - kwargs_ids[1] = rb_intern_const("N"); - kwargs_ids[2] = rb_intern_const("r"); - kwargs_ids[3] = rb_intern_const("p"); - kwargs_ids[4] = rb_intern_const("length"); + kwargs_ids[0] = rb_intern_const("salt"); + kwargs_ids[1] = rb_intern_const("N"); + kwargs_ids[2] = rb_intern_const("r"); + kwargs_ids[3] = rb_intern_const("p"); + kwargs_ids[4] = rb_intern_const("length"); } rb_scan_args(argc, argv, "1:", &pass, &opts); rb_get_kwargs(opts, kwargs_ids, 5, 0, kwargs); @@ -133,21 +131,20 @@ kdf_scrypt(int argc, VALUE *argv, VALUE self) str = rb_str_new(0, len); if (!EVP_PBE_scrypt(RSTRING_PTR(pass), RSTRING_LEN(pass), - (unsigned char *)RSTRING_PTR(salt), RSTRING_LEN(salt), - N, r, p, maxmem, (unsigned char *)RSTRING_PTR(str), len)) - ossl_raise(eKDF, "EVP_PBE_scrypt"); + (unsigned char *)RSTRING_PTR(salt), RSTRING_LEN(salt), + N, r, p, maxmem, (unsigned char *)RSTRING_PTR(str), len)) + ossl_raise(eKDF, "EVP_PBE_scrypt"); return str; } #endif -#if OSSL_OPENSSL_PREREQ(1, 1, 0) || OSSL_LIBRESSL_PREREQ(3, 6, 0) /* * call-seq: * KDF.hkdf(ikm, salt:, info:, length:, hash:) -> String * * HMAC-based Extract-and-Expand Key Derivation Function (HKDF) as specified in - * {RFC 5869}[https://tools.ietf.org/html/rfc5869]. + * {RFC 5869}[https://www.rfc-editor.org/rfc/rfc5869]. * * New in OpenSSL 1.1.0. * @@ -165,7 +162,7 @@ kdf_scrypt(int argc, VALUE *argv, VALUE self) * The hash function. * * === Example - * # The values from https://datatracker.ietf.org/doc/html/rfc5869#appendix-A.1 + * # The values from https://www.rfc-editor.org/rfc/rfc5869#appendix-A.1 * ikm = ["0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"].pack("H*") * salt = ["000102030405060708090a0b0c"].pack("H*") * info = ["f0f1f2f3f4f5f6f7f8f9"].pack("H*") @@ -175,7 +172,7 @@ kdf_scrypt(int argc, VALUE *argv, VALUE self) static VALUE kdf_hkdf(int argc, VALUE *argv, VALUE self) { - VALUE ikm, salt, info, opts, kwargs[4], str; + VALUE ikm, salt, info, opts, kwargs[4], str, md_holder; static ID kwargs_ids[4]; int saltlen, ikmlen, infolen; size_t len; @@ -183,10 +180,10 @@ kdf_hkdf(int argc, VALUE *argv, VALUE self) EVP_PKEY_CTX *pctx; if (!kwargs_ids[0]) { - kwargs_ids[0] = rb_intern_const("salt"); - kwargs_ids[1] = rb_intern_const("info"); - kwargs_ids[2] = rb_intern_const("length"); - kwargs_ids[3] = rb_intern_const("hash"); + kwargs_ids[0] = rb_intern_const("salt"); + kwargs_ids[1] = rb_intern_const("info"); + kwargs_ids[2] = rb_intern_const("length"); + kwargs_ids[3] = rb_intern_const("hash"); } rb_scan_args(argc, argv, "1:", &ikm, &opts); rb_get_kwargs(opts, kwargs_ids, 4, 0, kwargs); @@ -199,55 +196,49 @@ kdf_hkdf(int argc, VALUE *argv, VALUE self) infolen = RSTRING_LENINT(info); len = (size_t)NUM2LONG(kwargs[2]); if (len > LONG_MAX) - rb_raise(rb_eArgError, "length must be non-negative"); - md = ossl_evp_get_digestbyname(kwargs[3]); + rb_raise(rb_eArgError, "length must be non-negative"); + md = ossl_evp_md_fetch(kwargs[3], &md_holder); str = rb_str_new(NULL, (long)len); pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); if (!pctx) - ossl_raise(eKDF, "EVP_PKEY_CTX_new_id"); + ossl_raise(eKDF, "EVP_PKEY_CTX_new_id"); if (EVP_PKEY_derive_init(pctx) <= 0) { - EVP_PKEY_CTX_free(pctx); - ossl_raise(eKDF, "EVP_PKEY_derive_init"); + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_derive_init"); } if (EVP_PKEY_CTX_set_hkdf_md(pctx, md) <= 0) { - EVP_PKEY_CTX_free(pctx); - ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_md"); + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_md"); } if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (unsigned char *)RSTRING_PTR(salt), - saltlen) <= 0) { - EVP_PKEY_CTX_free(pctx); - ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_salt"); + saltlen) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_salt"); } if (EVP_PKEY_CTX_set1_hkdf_key(pctx, (unsigned char *)RSTRING_PTR(ikm), - ikmlen) <= 0) { - EVP_PKEY_CTX_free(pctx); - ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_key"); + ikmlen) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_key"); } if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (unsigned char *)RSTRING_PTR(info), - infolen) <= 0) { - EVP_PKEY_CTX_free(pctx); - ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_info"); + infolen) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_info"); } if (EVP_PKEY_derive(pctx, (unsigned char *)RSTRING_PTR(str), &len) <= 0) { - EVP_PKEY_CTX_free(pctx); - ossl_raise(eKDF, "EVP_PKEY_derive"); + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_derive"); } rb_str_set_len(str, (long)len); EVP_PKEY_CTX_free(pctx); return str; } -#endif void Init_ossl_kdf(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - /* * Document-module: OpenSSL::KDF * @@ -305,7 +296,5 @@ Init_ossl_kdf(void) #if defined(HAVE_EVP_PBE_SCRYPT) rb_define_module_function(mKDF, "scrypt", kdf_scrypt, -1); #endif -#if OSSL_OPENSSL_PREREQ(1, 1, 0) || OSSL_LIBRESSL_PREREQ(3, 6, 0) rb_define_module_function(mKDF, "hkdf", kdf_hkdf, -1); -#endif } diff --git a/ext/openssl/ossl_ns_spki.c b/ext/openssl/ossl_ns_spki.c index 9bed1f330e..8440c2ee82 100644 --- a/ext/openssl/ossl_ns_spki.c +++ b/ext/openssl/ossl_ns_spki.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -13,23 +13,23 @@ TypedData_Wrap_Struct((klass), &ossl_netscape_spki_type, 0) #define SetSPKI(obj, spki) do { \ if (!(spki)) { \ - ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (spki); \ } while (0) #define GetSPKI(obj, spki) do { \ TypedData_Get_Struct((obj), NETSCAPE_SPKI, &ossl_netscape_spki_type, (spki)); \ if (!(spki)) { \ - ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \ } \ } while (0) /* * Classes */ -VALUE mNetscape; -VALUE cSPKI; -VALUE eSPKIError; +static VALUE mNetscape; +static VALUE cSPKI; +static VALUE eSPKIError; /* * Public functions @@ -48,7 +48,7 @@ ossl_netscape_spki_free(void *spki) static const rb_data_type_t ossl_netscape_spki_type = { "OpenSSL/NETSCAPE_SPKI", { - 0, ossl_netscape_spki_free, + 0, ossl_netscape_spki_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -61,7 +61,7 @@ ossl_spki_alloc(VALUE klass) obj = NewSPKI(klass); if (!(spki = NETSCAPE_SPKI_new())) { - ossl_raise(eSPKIError, NULL); + ossl_raise(eSPKIError, NULL); } SetSPKI(obj, spki); @@ -83,15 +83,15 @@ ossl_spki_initialize(int argc, VALUE *argv, VALUE self) const unsigned char *p; if (rb_scan_args(argc, argv, "01", &buffer) == 0) { - return self; + return self; } StringValue(buffer); if (!(spki = NETSCAPE_SPKI_b64_decode(RSTRING_PTR(buffer), RSTRING_LENINT(buffer)))) { - ossl_clear_error(); - p = (unsigned char *)RSTRING_PTR(buffer); - if (!(spki = d2i_NETSCAPE_SPKI(NULL, &p, RSTRING_LEN(buffer)))) { - ossl_raise(eSPKIError, NULL); - } + ossl_clear_error(); + p = (unsigned char *)RSTRING_PTR(buffer); + if (!(spki = d2i_NETSCAPE_SPKI(NULL, &p, RSTRING_LEN(buffer)))) { + ossl_raise(eSPKIError, NULL); + } } NETSCAPE_SPKI_free(DATA_PTR(self)); SetSPKI(self, spki); @@ -115,11 +115,11 @@ ossl_spki_to_der(VALUE self) GetSPKI(self, spki); if ((len = i2d_NETSCAPE_SPKI(spki, NULL)) <= 0) - ossl_raise(eX509CertError, NULL); + ossl_raise(eSPKIError, "i2d_NETSCAPE_SPKI"); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_NETSCAPE_SPKI(spki, &p) <= 0) - ossl_raise(eX509CertError, NULL); + ossl_raise(eSPKIError, "i2d_NETSCAPE_SPKI"); ossl_str_adjust(str, p); return str; @@ -140,7 +140,7 @@ ossl_spki_to_pem(VALUE self) GetSPKI(self, spki); if (!(data = NETSCAPE_SPKI_b64_encode(spki))) { - ossl_raise(eSPKIError, NULL); + ossl_raise(eSPKIError, NULL); } str = ossl_buf2str(data, rb_long2int(strlen(data))); @@ -162,11 +162,11 @@ ossl_spki_print(VALUE self) GetSPKI(self, spki); if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eSPKIError, NULL); + ossl_raise(eSPKIError, NULL); } if (!NETSCAPE_SPKI_print(out, spki)) { - BIO_free(out); - ossl_raise(eSPKIError, NULL); + BIO_free(out); + ossl_raise(eSPKIError, NULL); } return ossl_membio2str(out); @@ -187,10 +187,10 @@ ossl_spki_get_public_key(VALUE self) GetSPKI(self, spki); if (!(pkey = NETSCAPE_SPKI_get_pubkey(spki))) { /* adds an reference */ - ossl_raise(eSPKIError, NULL); + ossl_raise(eSPKIError, NULL); } - return ossl_pkey_new(pkey); /* NO DUP - OK */ + return ossl_pkey_wrap(pkey); } /* @@ -214,7 +214,7 @@ ossl_spki_set_public_key(VALUE self, VALUE key) pkey = GetPKeyPtr(key); ossl_pkey_check_public_key(pkey); if (!NETSCAPE_SPKI_set_pubkey(spki, pkey)) - ossl_raise(eSPKIError, "NETSCAPE_SPKI_set_pubkey"); + ossl_raise(eSPKIError, "NETSCAPE_SPKI_set_pubkey"); return key; } @@ -230,13 +230,12 @@ ossl_spki_get_challenge(VALUE self) NETSCAPE_SPKI *spki; GetSPKI(self, spki); - if (spki->spkac->challenge->length <= 0) { - OSSL_Debug("Challenge.length <= 0?"); - return rb_str_new(0, 0); + if (ASN1_STRING_length(spki->spkac->challenge) <= 0) { + OSSL_Debug("Challenge.length <= 0?"); + return rb_str_new(0, 0); } - return rb_str_new((const char *)spki->spkac->challenge->data, - spki->spkac->challenge->length); + return asn1str_to_str(spki->spkac->challenge); } /* @@ -257,8 +256,8 @@ ossl_spki_set_challenge(VALUE self, VALUE str) StringValue(str); GetSPKI(self, spki); if (!ASN1_STRING_set(spki->spkac->challenge, RSTRING_PTR(str), - RSTRING_LENINT(str))) { - ossl_raise(eSPKIError, NULL); + RSTRING_LENINT(str))) { + ossl_raise(eSPKIError, NULL); } return str; @@ -283,13 +282,13 @@ ossl_spki_sign(VALUE self, VALUE key, VALUE digest) NETSCAPE_SPKI *spki; EVP_PKEY *pkey; const EVP_MD *md; + VALUE md_holder; pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ - md = ossl_evp_get_digestbyname(digest); + md = ossl_evp_md_fetch(digest, &md_holder); GetSPKI(self, spki); - if (!NETSCAPE_SPKI_sign(spki, pkey, md)) { - ossl_raise(eSPKIError, NULL); - } + if (!NETSCAPE_SPKI_sign(spki, pkey, md)) + ossl_raise(eSPKIError, "NETSCAPE_SPKI_sign"); return self; } @@ -315,12 +314,12 @@ ossl_spki_verify(VALUE self, VALUE key) ossl_pkey_check_public_key(pkey); switch (NETSCAPE_SPKI_verify(spki, pkey)) { case 0: - ossl_clear_error(); - return Qfalse; + ossl_clear_error(); + return Qfalse; case 1: - return Qtrue; + return Qtrue; default: - ossl_raise(eSPKIError, "NETSCAPE_SPKI_verify"); + ossl_raise(eSPKIError, "NETSCAPE_SPKI_verify"); } } @@ -365,8 +364,8 @@ ossl_spki_verify(VALUE self, VALUE key) * * OpenSSL::Netscape is a namespace for SPKI (Simple Public Key * Infrastructure) which implements Signed Public Key and Challenge. - * See {RFC 2692}[http://tools.ietf.org/html/rfc2692] and {RFC - * 2693}[http://tools.ietf.org/html/rfc2692] for details. + * See {RFC 2692}[https://www.rfc-editor.org/rfc/rfc2692] and {RFC + * 2693}[https://www.rfc-editor.org/rfc/rfc2692] for details. */ /* Document-class: OpenSSL::Netscape::SPKIError @@ -378,11 +377,6 @@ ossl_spki_verify(VALUE self, VALUE key) void Init_ossl_ns_spki(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - mNetscape = rb_define_module_under(mOSSL, "Netscape"); eSPKIError = rb_define_class_under(mNetscape, "SPKIError", eOSSLError); diff --git a/ext/openssl/ossl_ns_spki.h b/ext/openssl/ossl_ns_spki.h index 62ba8cb163..043b6cdb66 100644 --- a/ext/openssl/ossl_ns_spki.h +++ b/ext/openssl/ossl_ns_spki.h @@ -5,15 +5,11 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(_OSSL_NS_SPKI_H_) #define _OSSL_NS_SPKI_H_ -extern VALUE mNetscape; -extern VALUE cSPKI; -extern VALUE eSPKIError; - void Init_ossl_ns_spki(void); #endif /* _OSSL_NS_SPKI_H_ */ diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c index df986bb3ee..93d8bc8567 100644 --- a/ext/openssl/ossl_ocsp.c +++ b/ext/openssl/ossl_ocsp.c @@ -6,7 +6,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -67,13 +67,13 @@ if(!(cid)) ossl_raise(rb_eRuntimeError, "Cert ID wasn't initialized!"); \ } while (0) -VALUE mOCSP; -VALUE eOCSPError; -VALUE cOCSPReq; -VALUE cOCSPRes; -VALUE cOCSPBasicRes; -VALUE cOCSPSingleRes; -VALUE cOCSPCertId; +static VALUE mOCSP; +static VALUE eOCSPError; +static VALUE cOCSPReq; +static VALUE cOCSPRes; +static VALUE cOCSPBasicRes; +static VALUE cOCSPSingleRes; +static VALUE cOCSPCertId; static void ossl_ocsp_request_free(void *ptr) @@ -84,7 +84,7 @@ ossl_ocsp_request_free(void *ptr) static const rb_data_type_t ossl_ocsp_request_type = { "OpenSSL/OCSP/REQUEST", { - 0, ossl_ocsp_request_free, + 0, ossl_ocsp_request_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -98,7 +98,7 @@ ossl_ocsp_response_free(void *ptr) static const rb_data_type_t ossl_ocsp_response_type = { "OpenSSL/OCSP/RESPONSE", { - 0, ossl_ocsp_response_free, + 0, ossl_ocsp_response_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -112,7 +112,7 @@ ossl_ocsp_basicresp_free(void *ptr) static const rb_data_type_t ossl_ocsp_basicresp_type = { "OpenSSL/OCSP/BASICRESP", { - 0, ossl_ocsp_basicresp_free, + 0, ossl_ocsp_basicresp_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -126,7 +126,7 @@ ossl_ocsp_singleresp_free(void *ptr) static const rb_data_type_t ossl_ocsp_singleresp_type = { "OpenSSL/OCSP/SINGLERESP", { - 0, ossl_ocsp_singleresp_free, + 0, ossl_ocsp_singleresp_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -140,7 +140,7 @@ ossl_ocsp_certid_free(void *ptr) static const rb_data_type_t ossl_ocsp_certid_type = { "OpenSSL/OCSP/CERTID", { - 0, ossl_ocsp_certid_free, + 0, ossl_ocsp_certid_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -149,10 +149,14 @@ static const rb_data_type_t ossl_ocsp_certid_type = { * Public */ static VALUE -ossl_ocspcertid_new(OCSP_CERTID *cid) +ossl_ocspcid_new(const OCSP_CERTID *cid) { VALUE obj = NewOCSPCertId(cOCSPCertId); - SetOCSPCertId(obj, cid); + /* OpenSSL 1.1.1 takes a non-const pointer */ + OCSP_CERTID *cid_new = OCSP_CERTID_dup((OCSP_CERTID *)cid); + if (!cid_new) + ossl_raise(eOCSPError, "OCSP_CERTID_dup"); + SetOCSPCertId(obj, cid_new); return obj; } @@ -167,12 +171,13 @@ ossl_ocspreq_alloc(VALUE klass) obj = NewOCSPReq(klass); if (!(req = OCSP_REQUEST_new())) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); SetOCSPReq(obj, req); return obj; } +/* :nodoc: */ static VALUE ossl_ocspreq_initialize_copy(VALUE self, VALUE other) { @@ -184,7 +189,7 @@ ossl_ocspreq_initialize_copy(VALUE self, VALUE other) req_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_REQUEST), req); if (!req_new) - ossl_raise(eOCSPError, "ASN1_item_dup"); + ossl_raise(eOCSPError, "ASN1_item_dup"); SetOCSPReq(self, req_new); OCSP_REQUEST_free(req_old); @@ -210,15 +215,15 @@ ossl_ocspreq_initialize(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "01", &arg); if(!NIL_P(arg)){ - GetOCSPReq(self, req); - arg = ossl_to_der_if_possible(arg); - StringValue(arg); - p = (unsigned char *)RSTRING_PTR(arg); - req_new = d2i_OCSP_REQUEST(NULL, &p, RSTRING_LEN(arg)); - if (!req_new) - ossl_raise(eOCSPError, "d2i_OCSP_REQUEST"); - SetOCSPReq(self, req_new); - OCSP_REQUEST_free(req); + GetOCSPReq(self, req); + arg = ossl_to_der_if_possible(arg); + StringValue(arg); + p = (unsigned char *)RSTRING_PTR(arg); + req_new = d2i_OCSP_REQUEST(NULL, &p, RSTRING_LEN(arg)); + if (!req_new) + ossl_raise(eOCSPError, "d2i_OCSP_REQUEST"); + SetOCSPReq(self, req_new); + OCSP_REQUEST_free(req); } return self; @@ -244,13 +249,13 @@ ossl_ocspreq_add_nonce(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "01", &val); if(NIL_P(val)) { - GetOCSPReq(self, req); - ret = OCSP_request_add1_nonce(req, NULL, -1); + GetOCSPReq(self, req); + ret = OCSP_request_add1_nonce(req, NULL, -1); } else{ - StringValue(val); - GetOCSPReq(self, req); - ret = OCSP_request_add1_nonce(req, (unsigned char *)RSTRING_PTR(val), RSTRING_LENINT(val)); + StringValue(val); + GetOCSPReq(self, req); + ret = OCSP_request_add1_nonce(req, (unsigned char *)RSTRING_PTR(val), RSTRING_LENINT(val)); } if(!ret) ossl_raise(eOCSPError, NULL); @@ -307,10 +312,10 @@ ossl_ocspreq_add_certid(VALUE self, VALUE certid) GetOCSPCertId(certid, id); if (!(id_new = OCSP_CERTID_dup(id))) - ossl_raise(eOCSPError, "OCSP_CERTID_dup"); + ossl_raise(eOCSPError, "OCSP_CERTID_dup"); if (!OCSP_request_add0_id(req, id_new)) { - OCSP_CERTID_free(id_new); - ossl_raise(eOCSPError, "OCSP_request_add0_id"); + OCSP_CERTID_free(id_new); + ossl_raise(eOCSPError, "OCSP_request_add0_id"); } return self; @@ -327,21 +332,19 @@ static VALUE ossl_ocspreq_get_certid(VALUE self) { OCSP_REQUEST *req; - OCSP_ONEREQ *one; - OCSP_CERTID *id; - VALUE ary, tmp; - int i, count; GetOCSPReq(self, req); - count = OCSP_request_onereq_count(req); - ary = (count > 0) ? rb_ary_new() : Qnil; - for(i = 0; i < count; i++){ - one = OCSP_request_onereq_get0(req, i); - tmp = NewOCSPCertId(cOCSPCertId); - if(!(id = OCSP_CERTID_dup(OCSP_onereq_get0_id(one)))) - ossl_raise(eOCSPError, NULL); - SetOCSPCertId(tmp, id); - rb_ary_push(ary, tmp); + int count = OCSP_request_onereq_count(req); + if (count < 0) + ossl_raise(eOCSPError, "OCSP_request_onereq_count"); + if (count == 0) + return Qnil; + + VALUE ary = rb_ary_new_capa(count); + for (int i = 0; i < count; i++) { + OCSP_ONEREQ *one = OCSP_request_onereq_get0(req, i); + OCSP_CERTID *cid = OCSP_onereq_get0_id(one); + rb_ary_push(ary, ossl_ocspcid_new(cid)); } return ary; @@ -366,7 +369,7 @@ ossl_ocspreq_get_certid(VALUE self) static VALUE ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self) { - VALUE signer_cert, signer_key, certs, flags, digest; + VALUE signer_cert, signer_key, certs, flags, digest, md_holder; OCSP_REQUEST *req; X509 *signer; EVP_PKEY *key; @@ -380,19 +383,17 @@ ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self) signer = GetX509CertPtr(signer_cert); key = GetPrivPKeyPtr(signer_key); if (!NIL_P(flags)) - flg = NUM2INT(flags); - if (NIL_P(digest)) - md = NULL; - else - md = ossl_evp_get_digestbyname(digest); + flg = NUM2INT(flags); + md = NIL_P(digest) ? NULL : ossl_evp_md_fetch(digest, &md_holder); if (NIL_P(certs)) - flg |= OCSP_NOCERTS; + flg |= OCSP_NOCERTS; else - x509s = ossl_x509_ary2sk(certs); + x509s = ossl_x509_ary2sk(certs); ret = OCSP_request_sign(req, signer, key, md, x509s, flg); sk_X509_pop_free(x509s, X509_free); - if (!ret) ossl_raise(eOCSPError, NULL); + if (!ret) + ossl_raise(eOCSPError, "OCSP_request_sign"); return self; } @@ -426,7 +427,7 @@ ossl_ocspreq_verify(int argc, VALUE *argv, VALUE self) result = OCSP_request_verify(req, x509s, x509st, flg); sk_X509_pop_free(x509s, X509_free); if (result <= 0) - ossl_clear_error(); + ossl_clear_error(); return result > 0 ? Qtrue : Qfalse; } @@ -445,11 +446,11 @@ ossl_ocspreq_to_der(VALUE self) GetOCSPReq(self, req); if((len = i2d_OCSP_REQUEST(req, NULL)) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_OCSP_REQUEST(req, &p) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); ossl_str_adjust(str, p); return str; @@ -493,7 +494,7 @@ ossl_ocspres_s_create(VALUE klass, VALUE status, VALUE basic_resp) else GetOCSPBasicRes(basic_resp, bs); /* NO NEED TO DUP */ obj = NewOCSPRes(klass); if(!(res = OCSP_response_create(st, bs))) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); SetOCSPRes(obj, res); return obj; @@ -507,12 +508,13 @@ ossl_ocspres_alloc(VALUE klass) obj = NewOCSPRes(klass); if(!(res = OCSP_RESPONSE_new())) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); SetOCSPRes(obj, res); return obj; } +/* :nodoc: */ static VALUE ossl_ocspres_initialize_copy(VALUE self, VALUE other) { @@ -524,7 +526,7 @@ ossl_ocspres_initialize_copy(VALUE self, VALUE other) res_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_RESPONSE), res); if (!res_new) - ossl_raise(eOCSPError, "ASN1_item_dup"); + ossl_raise(eOCSPError, "ASN1_item_dup"); SetOCSPRes(self, res_new); OCSP_RESPONSE_free(res_old); @@ -550,15 +552,15 @@ ossl_ocspres_initialize(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "01", &arg); if(!NIL_P(arg)){ - GetOCSPRes(self, res); - arg = ossl_to_der_if_possible(arg); - StringValue(arg); - p = (unsigned char *)RSTRING_PTR(arg); - res_new = d2i_OCSP_RESPONSE(NULL, &p, RSTRING_LEN(arg)); - if (!res_new) - ossl_raise(eOCSPError, "d2i_OCSP_RESPONSE"); - SetOCSPRes(self, res_new); - OCSP_RESPONSE_free(res); + GetOCSPRes(self, res); + arg = ossl_to_der_if_possible(arg); + StringValue(arg); + p = (unsigned char *)RSTRING_PTR(arg); + res_new = d2i_OCSP_RESPONSE(NULL, &p, RSTRING_LEN(arg)); + if (!res_new) + ossl_raise(eOCSPError, "d2i_OCSP_RESPONSE"); + SetOCSPRes(self, res_new); + OCSP_RESPONSE_free(res); } return self; @@ -619,7 +621,7 @@ ossl_ocspres_get_basic(VALUE self) GetOCSPRes(self, res); ret = NewOCSPBasicRes(cOCSPBasicRes); if(!(bs = OCSP_response_get1_basic(res))) - return Qnil; + return Qnil; SetOCSPBasicRes(ret, bs); return ret; @@ -642,11 +644,11 @@ ossl_ocspres_to_der(VALUE self) GetOCSPRes(self, res); if((len = i2d_OCSP_RESPONSE(res, NULL)) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_OCSP_RESPONSE(res, &p) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); ossl_str_adjust(str, p); return str; @@ -663,12 +665,13 @@ ossl_ocspbres_alloc(VALUE klass) obj = NewOCSPBasicRes(klass); if(!(bs = OCSP_BASICRESP_new())) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); SetOCSPBasicRes(obj, bs); return obj; } +/* :nodoc: */ static VALUE ossl_ocspbres_initialize_copy(VALUE self, VALUE other) { @@ -680,7 +683,7 @@ ossl_ocspbres_initialize_copy(VALUE self, VALUE other) bs_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_BASICRESP), bs); if (!bs_new) - ossl_raise(eOCSPError, "ASN1_item_dup"); + ossl_raise(eOCSPError, "ASN1_item_dup"); SetOCSPBasicRes(self, bs_new); OCSP_BASICRESP_free(bs_old); @@ -705,15 +708,15 @@ ossl_ocspbres_initialize(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "01", &arg); if (!NIL_P(arg)) { - GetOCSPBasicRes(self, res); - arg = ossl_to_der_if_possible(arg); - StringValue(arg); - p = (unsigned char *)RSTRING_PTR(arg); - res_new = d2i_OCSP_BASICRESP(NULL, &p, RSTRING_LEN(arg)); - if (!res_new) - ossl_raise(eOCSPError, "d2i_OCSP_BASICRESP"); - SetOCSPBasicRes(self, res_new); - OCSP_BASICRESP_free(res); + GetOCSPBasicRes(self, res); + arg = ossl_to_der_if_possible(arg); + StringValue(arg); + p = (unsigned char *)RSTRING_PTR(arg); + res_new = d2i_OCSP_BASICRESP(NULL, &p, RSTRING_LEN(arg)); + if (!res_new) + ossl_raise(eOCSPError, "d2i_OCSP_BASICRESP"); + SetOCSPBasicRes(self, res_new); + OCSP_BASICRESP_free(res); } return self; @@ -758,13 +761,13 @@ ossl_ocspbres_add_nonce(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "01", &val); if(NIL_P(val)) { - GetOCSPBasicRes(self, bs); - ret = OCSP_basic_add1_nonce(bs, NULL, -1); + GetOCSPBasicRes(self, bs); + ret = OCSP_basic_add1_nonce(bs, NULL, -1); } else{ - StringValue(val); - GetOCSPBasicRes(self, bs); - ret = OCSP_basic_add1_nonce(bs, (unsigned char *)RSTRING_PTR(val), RSTRING_LENINT(val)); + StringValue(val); + GetOCSPBasicRes(self, bs); + ret = OCSP_basic_add1_nonce(bs, (unsigned char *)RSTRING_PTR(val), RSTRING_LENINT(val)); } if(!ret) ossl_raise(eOCSPError, NULL); @@ -777,12 +780,12 @@ add_status_convert_time(VALUE obj) ASN1_TIME *time; if (RB_INTEGER_TYPE_P(obj)) - time = X509_gmtime_adj(NULL, NUM2INT(obj)); + time = X509_gmtime_adj(NULL, NUM2INT(obj)); else - time = ossl_x509_time_adjust(NULL, obj); + time = ossl_x509_time_adjust(NULL, obj); if (!time) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); return (VALUE)time; } @@ -816,8 +819,8 @@ add_status_convert_time(VALUE obj) */ static VALUE ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status, - VALUE reason, VALUE revtime, - VALUE thisupd, VALUE nextupd, VALUE ext) + VALUE reason, VALUE revtime, + VALUE thisupd, VALUE nextupd, VALUE ext) { OCSP_BASICRESP *bs; OCSP_SINGLERESP *single; @@ -831,16 +834,16 @@ ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status, GetOCSPCertId(cid, id); st = NUM2INT(status); if (!NIL_P(ext)) { /* All ext's members must be X509::Extension */ - ext = rb_check_array_type(ext); - for (i = 0; i < RARRAY_LEN(ext); i++) - OSSL_Check_Kind(RARRAY_AREF(ext, i), cX509Ext); + ext = rb_check_array_type(ext); + for (i = 0; i < RARRAY_LEN(ext); i++) + OSSL_Check_Kind(RARRAY_AREF(ext, i), cX509Ext); } if (st == V_OCSP_CERTSTATUS_REVOKED) { - rsn = NUM2INT(reason); - tmp = rb_protect(add_status_convert_time, revtime, &rstatus); - if (rstatus) goto err; - rev = (ASN1_TIME *)tmp; + rsn = NUM2INT(reason); + tmp = rb_protect(add_status_convert_time, revtime, &rstatus); + if (rstatus) goto err; + rev = (ASN1_TIME *)tmp; } tmp = rb_protect(add_status_convert_time, thisupd, &rstatus); @@ -848,29 +851,29 @@ ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status, ths = (ASN1_TIME *)tmp; if (!NIL_P(nextupd)) { - tmp = rb_protect(add_status_convert_time, nextupd, &rstatus); - if (rstatus) goto err; - nxt = (ASN1_TIME *)tmp; + tmp = rb_protect(add_status_convert_time, nextupd, &rstatus); + if (rstatus) goto err; + nxt = (ASN1_TIME *)tmp; } if(!(single = OCSP_basic_add1_status(bs, id, st, rsn, rev, ths, nxt))){ - error = 1; - goto err; + error = 1; + goto err; } if(!NIL_P(ext)){ - X509_EXTENSION *x509ext; - - for(i = 0; i < RARRAY_LEN(ext); i++){ - x509ext = GetX509ExtPtr(RARRAY_AREF(ext, i)); - if(!OCSP_SINGLERESP_add_ext(single, x509ext, -1)){ - error = 1; - goto err; - } - } + X509_EXTENSION *x509ext; + + for(i = 0; i < RARRAY_LEN(ext); i++){ + x509ext = GetX509ExtPtr(RARRAY_AREF(ext, i)); + if(!OCSP_SINGLERESP_add_ext(single, x509ext, -1)){ + error = 1; + goto err; + } + } } - err: + err: ASN1_TIME_free(ths); ASN1_TIME_free(nxt); ASN1_TIME_free(rev); @@ -896,48 +899,40 @@ static VALUE ossl_ocspbres_get_status(VALUE self) { OCSP_BASICRESP *bs; - OCSP_SINGLERESP *single; - OCSP_CERTID *cid; - ASN1_TIME *revtime, *thisupd, *nextupd; - int status, reason; - X509_EXTENSION *x509ext; - VALUE ret, ary, ext; - int count, ext_count, i, j; GetOCSPBasicRes(self, bs); - ret = rb_ary_new(); - count = OCSP_resp_count(bs); - for(i = 0; i < count; i++){ - single = OCSP_resp_get0(bs, i); - if(!single) continue; - - revtime = thisupd = nextupd = NULL; - status = OCSP_single_get0_status(single, &reason, &revtime, - &thisupd, &nextupd); - if(status < 0) continue; - if(!(cid = OCSP_CERTID_dup((OCSP_CERTID *)OCSP_SINGLERESP_get0_id(single)))) /* FIXME */ - ossl_raise(eOCSPError, NULL); - ary = rb_ary_new(); - rb_ary_push(ary, ossl_ocspcertid_new(cid)); - rb_ary_push(ary, INT2NUM(status)); - rb_ary_push(ary, INT2NUM(reason)); - rb_ary_push(ary, revtime ? asn1time_to_time(revtime) : Qnil); - rb_ary_push(ary, thisupd ? asn1time_to_time(thisupd) : Qnil); - rb_ary_push(ary, nextupd ? asn1time_to_time(nextupd) : Qnil); - ext = rb_ary_new(); - ext_count = OCSP_SINGLERESP_get_ext_count(single); - for(j = 0; j < ext_count; j++){ - x509ext = OCSP_SINGLERESP_get_ext(single, j); - rb_ary_push(ext, ossl_x509ext_new(x509ext)); - } - rb_ary_push(ary, ext); - rb_ary_push(ret, ary); + VALUE ret = rb_ary_new(); + int count = OCSP_resp_count(bs); + for (int i = 0; i < count; i++) { + OCSP_SINGLERESP *single = OCSP_resp_get0(bs, i); + ASN1_TIME *revtime, *thisupd, *nextupd; + int reason; + + int status = OCSP_single_get0_status(single, &reason, &revtime, &thisupd, &nextupd); + if (status < 0) + ossl_raise(eOCSPError, "OCSP_single_get0_status"); + + VALUE ary = rb_ary_new(); + rb_ary_push(ary, ossl_ocspcid_new(OCSP_SINGLERESP_get0_id(single))); + rb_ary_push(ary, INT2NUM(status)); + rb_ary_push(ary, INT2NUM(reason)); + rb_ary_push(ary, revtime ? asn1time_to_time(revtime) : Qnil); + rb_ary_push(ary, thisupd ? asn1time_to_time(thisupd) : Qnil); + rb_ary_push(ary, nextupd ? asn1time_to_time(nextupd) : Qnil); + VALUE ext = rb_ary_new(); + int ext_count = OCSP_SINGLERESP_get_ext_count(single); + for (int j = 0; j < ext_count; j++) { + X509_EXTENSION *x509ext = OCSP_SINGLERESP_get_ext(single, j); + rb_ary_push(ext, ossl_x509ext_new(x509ext)); + } + rb_ary_push(ary, ext); + rb_ary_push(ret, ary); } return ret; } -static VALUE ossl_ocspsres_new(OCSP_SINGLERESP *); +static VALUE ossl_ocspsres_new(const OCSP_SINGLERESP *); /* * call-seq: @@ -955,17 +950,10 @@ ossl_ocspbres_get_responses(VALUE self) GetOCSPBasicRes(self, bs); count = OCSP_resp_count(bs); - ret = rb_ary_new2(count); + ret = rb_ary_new_capa(count); for (i = 0; i < count; i++) { - OCSP_SINGLERESP *sres, *sres_new; - - sres = OCSP_resp_get0(bs, i); - sres_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_SINGLERESP), sres); - if (!sres_new) - ossl_raise(eOCSPError, "ASN1_item_dup"); - - rb_ary_push(ret, ossl_ocspsres_new(sres_new)); + rb_ary_push(ret, ossl_ocspsres_new(OCSP_resp_get0(bs, i))); } return ret; @@ -983,7 +971,6 @@ static VALUE ossl_ocspbres_find_response(VALUE self, VALUE target) { OCSP_BASICRESP *bs; - OCSP_SINGLERESP *sres, *sres_new; OCSP_CERTID *id; int n; @@ -991,14 +978,8 @@ ossl_ocspbres_find_response(VALUE self, VALUE target) GetOCSPBasicRes(self, bs); if ((n = OCSP_resp_find(bs, id, -1)) == -1) - return Qnil; - - sres = OCSP_resp_get0(bs, n); - sres_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_SINGLERESP), sres); - if (!sres_new) - ossl_raise(eOCSPError, "ASN1_item_dup"); - - return ossl_ocspsres_new(sres_new); + return Qnil; + return ossl_ocspsres_new(OCSP_resp_get0(bs, n)); } /* @@ -1017,7 +998,7 @@ ossl_ocspbres_find_response(VALUE self, VALUE target) static VALUE ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self) { - VALUE signer_cert, signer_key, certs, flags, digest; + VALUE signer_cert, signer_key, certs, flags, digest, md_holder; OCSP_BASICRESP *bs; X509 *signer; EVP_PKEY *key; @@ -1031,19 +1012,17 @@ ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self) signer = GetX509CertPtr(signer_cert); key = GetPrivPKeyPtr(signer_key); if (!NIL_P(flags)) - flg = NUM2INT(flags); - if (NIL_P(digest)) - md = NULL; - else - md = ossl_evp_get_digestbyname(digest); + flg = NUM2INT(flags); + md = NIL_P(digest) ? NULL : ossl_evp_md_fetch(digest, &md_holder); if (NIL_P(certs)) - flg |= OCSP_NOCERTS; + flg |= OCSP_NOCERTS; else - x509s = ossl_x509_ary2sk(certs); + x509s = ossl_x509_ary2sk(certs); ret = OCSP_basic_sign(bs, signer, key, md, x509s, flg); sk_X509_pop_free(x509s, X509_free); - if (!ret) ossl_raise(eOCSPError, NULL); + if (!ret) + ossl_raise(eOCSPError, "OCSP_basic_sign"); return self; } @@ -1072,7 +1051,7 @@ ossl_ocspbres_verify(int argc, VALUE *argv, VALUE self) result = OCSP_basic_verify(bs, x509s, x509st, flg); sk_X509_pop_free(x509s, X509_free); if (result <= 0) - ossl_clear_error(); + ossl_clear_error(); return result > 0 ? Qtrue : Qfalse; } @@ -1093,11 +1072,11 @@ ossl_ocspbres_to_der(VALUE self) GetOCSPBasicRes(self, res); if ((len = i2d_OCSP_BASICRESP(res, NULL)) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_OCSP_BASICRESP(res, &p) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); ossl_str_adjust(str, p); return str; @@ -1107,12 +1086,18 @@ ossl_ocspbres_to_der(VALUE self) * OCSP::SingleResponse */ static VALUE -ossl_ocspsres_new(OCSP_SINGLERESP *sres) +ossl_ocspsres_new(const OCSP_SINGLERESP *sres) { VALUE obj; + OCSP_SINGLERESP *sres_new; obj = NewOCSPSingleRes(cOCSPSingleRes); - SetOCSPSingleRes(obj, sres); + /* OpenSSL 1.1.1 takes a non-const pointer */ + sres_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_SINGLERESP), + (OCSP_SINGLERESP *)sres); + if (!sres_new) + ossl_raise(eOCSPError, "ASN1_item_dup"); + SetOCSPSingleRes(obj, sres_new); return obj; } @@ -1125,7 +1110,7 @@ ossl_ocspsres_alloc(VALUE klass) obj = NewOCSPSingleRes(klass); if (!(sres = OCSP_SINGLERESP_new())) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); SetOCSPSingleRes(obj, sres); return obj; @@ -1150,13 +1135,14 @@ ossl_ocspsres_initialize(VALUE self, VALUE arg) p = (unsigned char*)RSTRING_PTR(arg); res_new = d2i_OCSP_SINGLERESP(NULL, &p, RSTRING_LEN(arg)); if (!res_new) - ossl_raise(eOCSPError, "d2i_OCSP_SINGLERESP"); + ossl_raise(eOCSPError, "d2i_OCSP_SINGLERESP"); SetOCSPSingleRes(self, res_new); OCSP_SINGLERESP_free(res); return self; } +/* :nodoc: */ static VALUE ossl_ocspsres_initialize_copy(VALUE self, VALUE other) { @@ -1168,7 +1154,7 @@ ossl_ocspsres_initialize_copy(VALUE self, VALUE other) sres_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_SINGLERESP), sres); if (!sres_new) - ossl_raise(eOCSPError, "ASN1_item_dup"); + ossl_raise(eOCSPError, "ASN1_item_dup"); SetOCSPSingleRes(self, sres_new); OCSP_SINGLERESP_free(sres_old); @@ -1207,15 +1193,15 @@ ossl_ocspsres_check_validity(int argc, VALUE *argv, VALUE self) GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, NULL, NULL, &this_update, &next_update); if (status < 0) - ossl_raise(eOCSPError, "OCSP_single_get0_status"); + ossl_raise(eOCSPError, "OCSP_single_get0_status"); ret = OCSP_check_validity(this_update, next_update, nsec, maxsec); if (ret) - return Qtrue; + return Qtrue; else { - ossl_clear_error(); - return Qfalse; + ossl_clear_error(); + return Qfalse; } } @@ -1229,12 +1215,9 @@ static VALUE ossl_ocspsres_get_certid(VALUE self) { OCSP_SINGLERESP *sres; - OCSP_CERTID *id; GetOCSPSingleRes(self, sres); - id = OCSP_CERTID_dup((OCSP_CERTID *)OCSP_SINGLERESP_get0_id(sres)); /* FIXME */ - - return ossl_ocspcertid_new(id); + return ossl_ocspcid_new(OCSP_SINGLERESP_get0_id(sres)); } /* @@ -1260,7 +1243,7 @@ ossl_ocspsres_get_cert_status(VALUE self) GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, NULL, NULL, NULL, NULL); if (status < 0) - ossl_raise(eOCSPError, "OCSP_single_get0_status"); + ossl_raise(eOCSPError, "OCSP_single_get0_status"); return INT2NUM(status); } @@ -1279,9 +1262,9 @@ ossl_ocspsres_get_this_update(VALUE self) GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, NULL, NULL, &time, NULL); if (status < 0) - ossl_raise(eOCSPError, "OCSP_single_get0_status"); + ossl_raise(eOCSPError, "OCSP_single_get0_status"); if (!time) - return Qnil; + return Qnil; return asn1time_to_time(time); } @@ -1300,9 +1283,9 @@ ossl_ocspsres_get_next_update(VALUE self) GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, NULL, NULL, NULL, &time); if (status < 0) - ossl_raise(eOCSPError, "OCSP_single_get0_status"); + ossl_raise(eOCSPError, "OCSP_single_get0_status"); if (!time) - return Qnil; + return Qnil; return asn1time_to_time(time); } @@ -1321,11 +1304,11 @@ ossl_ocspsres_get_revocation_time(VALUE self) GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, NULL, &time, NULL, NULL); if (status < 0) - ossl_raise(eOCSPError, "OCSP_single_get0_status"); + ossl_raise(eOCSPError, "OCSP_single_get0_status"); if (status != V_OCSP_CERTSTATUS_REVOKED) - ossl_raise(eOCSPError, "certificate is not revoked"); + ossl_raise(eOCSPError, "certificate is not revoked"); if (!time) - return Qnil; + return Qnil; return asn1time_to_time(time); } @@ -1343,9 +1326,9 @@ ossl_ocspsres_get_revocation_reason(VALUE self) GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, &reason, NULL, NULL, NULL); if (status < 0) - ossl_raise(eOCSPError, "OCSP_single_get0_status"); + ossl_raise(eOCSPError, "OCSP_single_get0_status"); if (status != V_OCSP_CERTSTATUS_REVOKED) - ossl_raise(eOCSPError, "certificate is not revoked"); + ossl_raise(eOCSPError, "certificate is not revoked"); return INT2NUM(reason); } @@ -1367,8 +1350,8 @@ ossl_ocspsres_get_extensions(VALUE self) count = OCSP_SINGLERESP_get_ext_count(sres); ary = rb_ary_new2(count); for (i = 0; i < count; i++) { - ext = OCSP_SINGLERESP_get_ext(sres, i); - rb_ary_push(ary, ossl_x509ext_new(ext)); /* will dup */ + ext = OCSP_SINGLERESP_get_ext(sres, i); + rb_ary_push(ary, ossl_x509ext_new(ext)); /* will dup */ } return ary; @@ -1390,11 +1373,11 @@ ossl_ocspsres_to_der(VALUE self) GetOCSPSingleRes(self, sres); if ((len = i2d_OCSP_SINGLERESP(sres, NULL)) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_OCSP_SINGLERESP(sres, &p) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); ossl_str_adjust(str, p); return str; @@ -1412,12 +1395,13 @@ ossl_ocspcid_alloc(VALUE klass) obj = NewOCSPCertId(klass); if(!(id = OCSP_CERTID_new())) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); SetOCSPCertId(obj, id); return obj; } +/* :nodoc: */ static VALUE ossl_ocspcid_initialize_copy(VALUE self, VALUE other) { @@ -1429,7 +1413,7 @@ ossl_ocspcid_initialize_copy(VALUE self, VALUE other) cid_new = OCSP_CERTID_dup(cid); if (!cid_new) - ossl_raise(eOCSPError, "OCSP_CERTID_dup"); + ossl_raise(eOCSPError, "OCSP_CERTID_dup"); SetOCSPCertId(self, cid_new); OCSP_CERTID_free(cid_old); @@ -1459,27 +1443,28 @@ ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self) GetOCSPCertId(self, id); if (rb_scan_args(argc, argv, "12", &subject, &issuer, &digest) == 1) { - VALUE arg; - const unsigned char *p; - - arg = ossl_to_der_if_possible(subject); - StringValue(arg); - p = (unsigned char *)RSTRING_PTR(arg); - newid = d2i_OCSP_CERTID(NULL, &p, RSTRING_LEN(arg)); - if (!newid) - ossl_raise(eOCSPError, "d2i_OCSP_CERTID"); + VALUE arg; + const unsigned char *p; + + arg = ossl_to_der_if_possible(subject); + StringValue(arg); + p = (unsigned char *)RSTRING_PTR(arg); + newid = d2i_OCSP_CERTID(NULL, &p, RSTRING_LEN(arg)); + if (!newid) + ossl_raise(eOCSPError, "d2i_OCSP_CERTID"); } else { - X509 *x509s, *x509i; - const EVP_MD *md; + X509 *x509s, *x509i; + const EVP_MD *md; + VALUE md_holder; - x509s = GetX509CertPtr(subject); /* NO NEED TO DUP */ - x509i = GetX509CertPtr(issuer); /* NO NEED TO DUP */ - md = !NIL_P(digest) ? ossl_evp_get_digestbyname(digest) : NULL; + x509s = GetX509CertPtr(subject); /* NO NEED TO DUP */ + x509i = GetX509CertPtr(issuer); /* NO NEED TO DUP */ + md = NIL_P(digest) ? NULL : ossl_evp_md_fetch(digest, &md_holder); - newid = OCSP_cert_to_id(md, x509s, x509i); - if (!newid) - ossl_raise(eOCSPError, "OCSP_cert_to_id"); + newid = OCSP_cert_to_id(md, x509s, x509i); + if (!newid) + ossl_raise(eOCSPError, "OCSP_cert_to_id"); } SetOCSPCertId(self, newid); @@ -1565,8 +1550,9 @@ ossl_ocspcid_get_issuer_name_hash(VALUE self) GetOCSPCertId(self, id); OCSP_id_get0_info(&name_hash, NULL, NULL, NULL, id); - ret = rb_str_new(NULL, name_hash->length * 2); - ossl_bin2hex(name_hash->data, RSTRING_PTR(ret), name_hash->length); + ret = rb_str_new(NULL, ASN1_STRING_length(name_hash) * 2); + ossl_bin2hex(ASN1_STRING_get0_data(name_hash), RSTRING_PTR(ret), + ASN1_STRING_length(name_hash)); return ret; } @@ -1588,8 +1574,9 @@ ossl_ocspcid_get_issuer_key_hash(VALUE self) GetOCSPCertId(self, id); OCSP_id_get0_info(NULL, NULL, &key_hash, NULL, id); - ret = rb_str_new(NULL, key_hash->length * 2); - ossl_bin2hex(key_hash->data, RSTRING_PTR(ret), key_hash->length); + ret = rb_str_new(NULL, ASN1_STRING_length(key_hash) * 2); + ossl_bin2hex(ASN1_STRING_get0_data(key_hash), RSTRING_PTR(ret), + ASN1_STRING_length(key_hash)); return ret; } @@ -1606,19 +1593,10 @@ ossl_ocspcid_get_hash_algorithm(VALUE self) { OCSP_CERTID *id; ASN1_OBJECT *oid; - BIO *out; GetOCSPCertId(self, id); OCSP_id_get0_info(NULL, &oid, NULL, NULL, id); - - if (!(out = BIO_new(BIO_s_mem()))) - ossl_raise(eOCSPError, "BIO_new"); - - if (!i2a_ASN1_OBJECT(out, oid)) { - BIO_free(out); - ossl_raise(eOCSPError, "i2a_ASN1_OBJECT"); - } - return ossl_membio2str(out); + return ossl_asn1obj_to_string_long_name(oid); } /* @@ -1637,11 +1615,11 @@ ossl_ocspcid_to_der(VALUE self) GetOCSPCertId(self, id); if ((len = i2d_OCSP_CERTID(id, NULL)) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_OCSP_CERTID(id, &p) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); ossl_str_adjust(str, p); return str; @@ -1650,11 +1628,6 @@ ossl_ocspcid_to_der(VALUE self) void Init_ossl_ocsp(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - /* * OpenSSL::OCSP implements Online Certificate Status Protocol requests * and responses. diff --git a/ext/openssl/ossl_ocsp.h b/ext/openssl/ossl_ocsp.h index 6d2aac8657..becd70ffed 100644 --- a/ext/openssl/ossl_ocsp.h +++ b/ext/openssl/ossl_ocsp.h @@ -6,18 +6,11 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(_OSSL_OCSP_H_) #define _OSSL_OCSP_H_ -#if !defined(OPENSSL_NO_OCSP) -extern VALUE mOCSP; -extern VALUE cOCSPReq; -extern VALUE cOCSPRes; -extern VALUE cOCSPBasicRes; -#endif - void Init_ossl_ocsp(void); #endif /* _OSSL_OCSP_H_ */ diff --git a/ext/openssl/ossl_pkcs12.c b/ext/openssl/ossl_pkcs12.c index 164b2da465..32b82a881c 100644 --- a/ext/openssl/ossl_pkcs12.c +++ b/ext/openssl/ossl_pkcs12.c @@ -1,6 +1,6 @@ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -27,8 +27,8 @@ /* * Classes */ -VALUE cPKCS12; -VALUE ePKCS12Error; +static VALUE cPKCS12; +static VALUE ePKCS12Error; /* * Private @@ -42,7 +42,7 @@ ossl_pkcs12_free(void *ptr) static const rb_data_type_t ossl_pkcs12_type = { "OpenSSL/PKCS12", { - 0, ossl_pkcs12_free, + 0, ossl_pkcs12_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -60,6 +60,7 @@ ossl_pkcs12_s_allocate(VALUE klass) return obj; } +/* :nodoc: */ static VALUE ossl_pkcs12_initialize_copy(VALUE self, VALUE other) { @@ -71,7 +72,7 @@ ossl_pkcs12_initialize_copy(VALUE self, VALUE other) p12_new = ASN1_dup((i2d_of_void *)i2d_PKCS12, (d2i_of_void *)d2i_PKCS12, (char *)p12); if (!p12_new) - ossl_raise(ePKCS12Error, "ASN1_dup"); + ossl_raise(ePKCS12Error, "ASN1_dup"); SetPKCS12(self, p12_new); PKCS12_free(p12_old); @@ -121,11 +122,11 @@ ossl_pkcs12_s_create(int argc, VALUE *argv, VALUE self) /* TODO: make a VALUE to nid function */ if (!NIL_P(key_nid)) { if ((nkey = OBJ_txt2nid(StringValueCStr(key_nid))) == NID_undef) - ossl_raise(rb_eArgError, "Unknown PBE algorithm %"PRIsVALUE, key_nid); + ossl_raise(rb_eArgError, "Unknown PBE algorithm %"PRIsVALUE, key_nid); } if (!NIL_P(cert_nid)) { if ((ncert = OBJ_txt2nid(StringValueCStr(cert_nid))) == NID_undef) - ossl_raise(rb_eArgError, "Unknown PBE algorithm %"PRIsVALUE, cert_nid); + ossl_raise(rb_eArgError, "Unknown PBE algorithm %"PRIsVALUE, cert_nid); } if (!NIL_P(key_iter)) kiter = NUM2INT(key_iter); @@ -134,6 +135,16 @@ ossl_pkcs12_s_create(int argc, VALUE *argv, VALUE self) if (!NIL_P(keytype)) ktype = NUM2INT(keytype); +#if defined(OPENSSL_IS_AWSLC) + if (ktype != 0) { + ossl_raise(rb_eArgError, "Unknown key usage type %"PRIsVALUE, INT2NUM(ktype)); + } +#else + if (ktype != 0 && ktype != KEY_SIG && ktype != KEY_EX) { + ossl_raise(rb_eArgError, "Unknown key usage type %"PRIsVALUE, INT2NUM(ktype)); + } +#endif + obj = NewPKCS12(cPKCS12); x509s = NIL_P(ca) ? NULL : ossl_x509_ary2sk(ca); p12 = PKCS12_create(passphrase, friendlyname, key, x509, x509s, @@ -150,9 +161,9 @@ ossl_pkcs12_s_create(int argc, VALUE *argv, VALUE self) } static VALUE -ossl_pkey_new_i(VALUE arg) +ossl_pkey_wrap_i(VALUE arg) { - return ossl_pkey_new((EVP_PKEY *)arg); + return ossl_pkey_wrap((EVP_PKEY *)arg); } static VALUE @@ -197,23 +208,19 @@ ossl_pkcs12_initialize(int argc, VALUE *argv, VALUE self) BIO_free(in); pkey = cert = ca = Qnil; - /* OpenSSL's bug; PKCS12_parse() puts errors even if it succeeds. - * Fixed in OpenSSL 1.0.0t, 1.0.1p, 1.0.2d */ - ERR_set_mark(); if(!PKCS12_parse(pkcs, passphrase, &key, &x509, &x509s)) - ossl_raise(ePKCS12Error, "PKCS12_parse"); - ERR_pop_to_mark(); + ossl_raise(ePKCS12Error, "PKCS12_parse"); if (key) { - pkey = rb_protect(ossl_pkey_new_i, (VALUE)key, &st); - if (st) goto err; + pkey = rb_protect(ossl_pkey_wrap_i, (VALUE)key, &st); + if (st) goto err; } if (x509) { - cert = rb_protect(ossl_x509_new_i, (VALUE)x509, &st); - if (st) goto err; + cert = rb_protect(ossl_x509_new_i, (VALUE)x509, &st); + if (st) goto err; } if (x509s) { - ca = rb_protect(ossl_x509_sk2ary_i, (VALUE)x509s, &st); - if (st) goto err; + ca = rb_protect(ossl_x509_sk2ary_i, (VALUE)x509s, &st); + if (st) goto err; } err: @@ -237,25 +244,62 @@ ossl_pkcs12_to_der(VALUE self) GetPKCS12(self, p12); if((len = i2d_PKCS12(p12, NULL)) <= 0) - ossl_raise(ePKCS12Error, NULL); + ossl_raise(ePKCS12Error, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_PKCS12(p12, &p) <= 0) - ossl_raise(ePKCS12Error, NULL); + ossl_raise(ePKCS12Error, NULL); ossl_str_adjust(str, p); return str; } +/* + * call-seq: + * pkcs12.set_mac(pass, salt = nil, iter = nil, md_type = nil) + * + * Sets MAC parameters and generates MAC over the PKCS #12 structure. + * + * This method uses HMAC and the PKCS #12 specific password-based KDF as + * specified in the original PKCS #12. + * + * See also the man page PKCS12_set_mac(3). + * + * Added in version 3.3.0. + */ +static VALUE +pkcs12_set_mac(int argc, VALUE *argv, VALUE self) +{ + PKCS12 *p12; + VALUE pass, salt, iter, md_name, md_holder = Qnil; + int iter_i = 0; + const EVP_MD *md_type = NULL; + + rb_scan_args(argc, argv, "13", &pass, &salt, &iter, &md_name); + rb_check_frozen(self); + GetPKCS12(self, p12); + + StringValue(pass); + if (!NIL_P(salt)) + StringValue(salt); + if (!NIL_P(iter)) + iter_i = NUM2INT(iter); + if (!NIL_P(md_name)) + md_type = ossl_evp_md_fetch(md_name, &md_holder); + + if (!PKCS12_set_mac(p12, RSTRING_PTR(pass), RSTRING_LENINT(pass), + !NIL_P(salt) ? (unsigned char *)RSTRING_PTR(salt) : NULL, + !NIL_P(salt) ? RSTRING_LENINT(salt) : 0, + iter_i, md_type)) + ossl_raise(ePKCS12Error, "PKCS12_set_mac"); + + return Qnil; +} + void Init_ossl_pkcs12(void) { #undef rb_intern -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - /* * Defines a file format commonly used to store private keys with * accompanying public key certificates, protected with a password-based @@ -272,4 +316,11 @@ Init_ossl_pkcs12(void) rb_attr(cPKCS12, rb_intern("ca_certs"), 1, 0, Qfalse); rb_define_method(cPKCS12, "initialize", ossl_pkcs12_initialize, -1); rb_define_method(cPKCS12, "to_der", ossl_pkcs12_to_der, 0); + rb_define_method(cPKCS12, "set_mac", pkcs12_set_mac, -1); + +#if !defined(OPENSSL_IS_AWSLC) + /* MSIE specific PKCS12 key usage extensions */ + rb_define_const(cPKCS12, "KEY_EX", INT2NUM(KEY_EX)); + rb_define_const(cPKCS12, "KEY_SIG", INT2NUM(KEY_SIG)); +#endif } diff --git a/ext/openssl/ossl_pkcs12.h b/ext/openssl/ossl_pkcs12.h index fe4f15ef60..6d2cd901cb 100644 --- a/ext/openssl/ossl_pkcs12.h +++ b/ext/openssl/ossl_pkcs12.h @@ -1,13 +1,10 @@ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(_OSSL_PKCS12_H_) #define _OSSL_PKCS12_H_ -extern VALUE cPKCS12; -extern VALUE ePKCS12Error; - void Init_ossl_pkcs12(void); #endif /* _OSSL_PKCS12_H_ */ diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c index 78dcbd667a..6e51fd42b9 100644 --- a/ext/openssl/ossl_pkcs7.c +++ b/ext/openssl/ossl_pkcs7.c @@ -5,22 +5,37 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" +#define NewPKCS7(klass) \ + TypedData_Wrap_Struct((klass), &ossl_pkcs7_type, 0) +#define SetPKCS7(obj, pkcs7) do { \ + if (!(pkcs7)) { \ + ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \ + } \ + RTYPEDDATA_DATA(obj) = (pkcs7); \ +} while (0) +#define GetPKCS7(obj, pkcs7) do { \ + TypedData_Get_Struct((obj), PKCS7, &ossl_pkcs7_type, (pkcs7)); \ + if (!(pkcs7)) { \ + ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \ + } \ +} while (0) + #define NewPKCS7si(klass) \ TypedData_Wrap_Struct((klass), &ossl_pkcs7_signer_info_type, 0) #define SetPKCS7si(obj, p7si) do { \ if (!(p7si)) { \ - ossl_raise(rb_eRuntimeError, "PKCS7si wasn't initialized."); \ + ossl_raise(rb_eRuntimeError, "PKCS7si wasn't initialized."); \ } \ RTYPEDDATA_DATA(obj) = (p7si); \ } while (0) #define GetPKCS7si(obj, p7si) do { \ TypedData_Get_Struct((obj), PKCS7_SIGNER_INFO, &ossl_pkcs7_signer_info_type, (p7si)); \ if (!(p7si)) { \ - ossl_raise(rb_eRuntimeError, "PKCS7si wasn't initialized."); \ + ossl_raise(rb_eRuntimeError, "PKCS7si wasn't initialized."); \ } \ } while (0) @@ -28,14 +43,14 @@ TypedData_Wrap_Struct((klass), &ossl_pkcs7_recip_info_type, 0) #define SetPKCS7ri(obj, p7ri) do { \ if (!(p7ri)) { \ - ossl_raise(rb_eRuntimeError, "PKCS7ri wasn't initialized."); \ + ossl_raise(rb_eRuntimeError, "PKCS7ri wasn't initialized."); \ } \ RTYPEDDATA_DATA(obj) = (p7ri); \ } while (0) #define GetPKCS7ri(obj, p7ri) do { \ TypedData_Get_Struct((obj), PKCS7_RECIP_INFO, &ossl_pkcs7_recip_info_type, (p7ri)); \ if (!(p7ri)) { \ - ossl_raise(rb_eRuntimeError, "PKCS7ri wasn't initialized."); \ + ossl_raise(rb_eRuntimeError, "PKCS7ri wasn't initialized."); \ } \ } while (0) @@ -49,10 +64,11 @@ /* * Classes */ -VALUE cPKCS7; -VALUE cPKCS7Signer; -VALUE cPKCS7Recipient; -VALUE ePKCS7Error; +static VALUE cPKCS7; +static VALUE cPKCS7Signer; +static VALUE cPKCS7Recipient; +static VALUE ePKCS7Error; +static ID id_md_holder, id_cipher_holder; static void ossl_pkcs7_free(void *ptr) @@ -60,14 +76,28 @@ ossl_pkcs7_free(void *ptr) PKCS7_free(ptr); } -const rb_data_type_t ossl_pkcs7_type = { +static const rb_data_type_t ossl_pkcs7_type = { "OpenSSL/PKCS7", { - 0, ossl_pkcs7_free, + 0, ossl_pkcs7_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; +VALUE +ossl_pkcs7_new(PKCS7 *p7) +{ + PKCS7 *new; + VALUE obj = NewPKCS7(cPKCS7); + + new = PKCS7_dup(p7); + if (!new) + ossl_raise(ePKCS7Error, "PKCS7_dup"); + SetPKCS7(obj, new); + + return obj; +} + static void ossl_pkcs7_signer_info_free(void *ptr) { @@ -77,7 +107,7 @@ ossl_pkcs7_signer_info_free(void *ptr) static const rb_data_type_t ossl_pkcs7_signer_info_type = { "OpenSSL/PKCS7/SIGNER_INFO", { - 0, ossl_pkcs7_signer_info_free, + 0, ossl_pkcs7_signer_info_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -91,7 +121,7 @@ ossl_pkcs7_recip_info_free(void *ptr) static const rb_data_type_t ossl_pkcs7_recip_info_type = { "OpenSSL/PKCS7/RECIP_INFO", { - 0, ossl_pkcs7_recip_info_free, + 0, ossl_pkcs7_recip_info_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -114,23 +144,32 @@ ossl_PKCS7_SIGNER_INFO_dup(PKCS7_SIGNER_INFO *si) } static PKCS7_RECIP_INFO * -ossl_PKCS7_RECIP_INFO_dup(PKCS7_RECIP_INFO *si) +ossl_PKCS7_RECIP_INFO_dup(PKCS7_RECIP_INFO *ri) { - return ASN1_dup((i2d_of_void *)i2d_PKCS7_RECIP_INFO, - (d2i_of_void *)d2i_PKCS7_RECIP_INFO, - si); + PKCS7_RECIP_INFO *ri_new = ASN1_dup((i2d_of_void *)i2d_PKCS7_RECIP_INFO, + (d2i_of_void *)d2i_PKCS7_RECIP_INFO, + ri); + if (ri_new && ri->cert) { + if (!X509_up_ref(ri->cert)) { + PKCS7_RECIP_INFO_free(ri_new); + return NULL; + } + ri_new->cert = ri->cert; + } + return ri_new; } static VALUE ossl_pkcs7si_new(PKCS7_SIGNER_INFO *p7si) { - PKCS7_SIGNER_INFO *pkcs7; + PKCS7_SIGNER_INFO *p7si_new; VALUE obj; obj = NewPKCS7si(cPKCS7Signer); - pkcs7 = p7si ? ossl_PKCS7_SIGNER_INFO_dup(p7si) : PKCS7_SIGNER_INFO_new(); - if (!pkcs7) ossl_raise(ePKCS7Error, NULL); - SetPKCS7si(obj, pkcs7); + p7si_new = ossl_PKCS7_SIGNER_INFO_dup(p7si); + if (!p7si_new) + ossl_raise(ePKCS7Error, "ASN1_dup"); + SetPKCS7si(obj, p7si_new); return obj; } @@ -138,13 +177,14 @@ ossl_pkcs7si_new(PKCS7_SIGNER_INFO *p7si) static VALUE ossl_pkcs7ri_new(PKCS7_RECIP_INFO *p7ri) { - PKCS7_RECIP_INFO *pkcs7; + PKCS7_RECIP_INFO *p7ri_new; VALUE obj; obj = NewPKCS7ri(cPKCS7Recipient); - pkcs7 = p7ri ? ossl_PKCS7_RECIP_INFO_dup(p7ri) : PKCS7_RECIP_INFO_new(); - if (!pkcs7) ossl_raise(ePKCS7Error, NULL); - SetPKCS7ri(obj, pkcs7); + p7ri_new = ossl_PKCS7_RECIP_INFO_dup(p7ri); + if (!p7ri_new) + ossl_raise(ePKCS7Error,"ASN1_dup"); + SetPKCS7ri(obj, p7ri_new); return obj; } @@ -165,7 +205,13 @@ ossl_pkcs7_s_read_smime(VALUE klass, VALUE arg) out = NULL; pkcs7 = SMIME_read_PKCS7(in, &out); BIO_free(in); - if(!pkcs7) ossl_raise(ePKCS7Error, NULL); + if (!pkcs7) + ossl_raise(ePKCS7Error, "Could not parse the PKCS7"); + if (!pkcs7->d.ptr) { + PKCS7_free(pkcs7); + ossl_raise(ePKCS7Error, "No content in PKCS7"); + } + data = out ? ossl_membio2str(out) : Qnil; SetPKCS7(ret, pkcs7); ossl_pkcs7_set_data(ret, data); @@ -192,7 +238,7 @@ ossl_pkcs7_s_write_smime(int argc, VALUE *argv, VALUE klass) if(NIL_P(data)) data = ossl_pkcs7_get_data(pkcs7); GetPKCS7(pkcs7, p7); if(!NIL_P(data) && PKCS7_is_detached(p7)) - flg |= PKCS7_DETACHED; + flg |= PKCS7_DETACHED; in = NIL_P(data) ? NULL : ossl_obj2bio(&data); if(!(out = BIO_new(BIO_s_mem()))){ BIO_free(in); @@ -233,16 +279,16 @@ ossl_pkcs7_s_sign(int argc, VALUE *argv, VALUE klass) in = ossl_obj2bio(&data); if(NIL_P(certs)) x509s = NULL; else{ - x509s = ossl_protect_x509_ary2sk(certs, &status); - if(status){ - BIO_free(in); - rb_jump_tag(status); - } + x509s = ossl_protect_x509_ary2sk(certs, &status); + if(status){ + BIO_free(in); + rb_jump_tag(status); + } } if(!(pkcs7 = PKCS7_sign(x509, pkey, x509s, in, flg))){ - BIO_free(in); - sk_X509_pop_free(x509s, X509_free); - ossl_raise(ePKCS7Error, NULL); + BIO_free(in); + sk_X509_pop_free(x509s, X509_free); + ossl_raise(ePKCS7Error, NULL); } SetPKCS7(ret, pkcs7); ossl_pkcs7_set_data(ret, data); @@ -255,12 +301,19 @@ ossl_pkcs7_s_sign(int argc, VALUE *argv, VALUE klass) /* * call-seq: - * PKCS7.encrypt(certs, data, [, cipher [, flags]]) => pkcs7 + * PKCS7.encrypt(certs, data, cipher, flags = 0) => pkcs7 + * + * Creates a PKCS #7 enveloped-data structure. + * + * Before version 3.3.0, +cipher+ was optional and defaulted to + * <tt>"RC2-40-CBC"</tt>. + * + * See also the man page PKCS7_encrypt(3). */ static VALUE ossl_pkcs7_s_encrypt(int argc, VALUE *argv, VALUE klass) { - VALUE certs, data, cipher, flags; + VALUE certs, data, cipher, flags, cipher_holder; STACK_OF(X509) *x509s; BIO *in; const EVP_CIPHER *ciph; @@ -269,37 +322,29 @@ ossl_pkcs7_s_encrypt(int argc, VALUE *argv, VALUE klass) PKCS7 *p7; rb_scan_args(argc, argv, "22", &certs, &data, &cipher, &flags); - if(NIL_P(cipher)){ -#if !defined(OPENSSL_NO_RC2) - ciph = EVP_rc2_40_cbc(); -#elif !defined(OPENSSL_NO_DES) - ciph = EVP_des_ede3_cbc(); -#elif !defined(OPENSSL_NO_RC2) - ciph = EVP_rc2_40_cbc(); -#elif !defined(OPENSSL_NO_AES) - ciph = EVP_EVP_aes_128_cbc(); -#else - ossl_raise(ePKCS7Error, "Must specify cipher"); -#endif - + if (NIL_P(cipher)) { + rb_raise(rb_eArgError, + "cipher must be specified. Before version 3.3, " \ + "the default cipher was RC2-40-CBC."); } - else ciph = ossl_evp_get_cipherbyname(cipher); + ciph = ossl_evp_cipher_fetch(cipher, &cipher_holder); flg = NIL_P(flags) ? 0 : NUM2INT(flags); ret = NewPKCS7(cPKCS7); in = ossl_obj2bio(&data); x509s = ossl_protect_x509_ary2sk(certs, &status); if(status){ - BIO_free(in); - rb_jump_tag(status); + BIO_free(in); + rb_jump_tag(status); } - if(!(p7 = PKCS7_encrypt(x509s, in, (EVP_CIPHER*)ciph, flg))){ - BIO_free(in); - sk_X509_pop_free(x509s, X509_free); - ossl_raise(ePKCS7Error, NULL); + if (!(p7 = PKCS7_encrypt(x509s, in, ciph, flg))) { + BIO_free(in); + sk_X509_pop_free(x509s, X509_free); + ossl_raise(ePKCS7Error, NULL); } BIO_free(in); SetPKCS7(ret, p7); ossl_pkcs7_set_data(ret, data); + rb_ivar_set(ret, id_cipher_holder, cipher_holder); sk_X509_pop_free(x509s, X509_free); return ret; @@ -313,7 +358,7 @@ ossl_pkcs7_alloc(VALUE klass) obj = NewPKCS7(klass); if (!(pkcs7 = PKCS7_new())) { - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } SetPKCS7(obj, pkcs7); @@ -335,7 +380,7 @@ ossl_pkcs7_initialize(int argc, VALUE *argv, VALUE self) VALUE arg; if(rb_scan_args(argc, argv, "01", &arg) == 0) - return self; + return self; arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); p7 = d2i_PKCS7_bio(in, NULL); @@ -345,7 +390,11 @@ ossl_pkcs7_initialize(int argc, VALUE *argv, VALUE self) } BIO_free(in); if (!p7) - ossl_raise(rb_eArgError, "Could not parse the PKCS7"); + ossl_raise(ePKCS7Error, "Could not parse the PKCS7"); + if (!p7->d.ptr) { + PKCS7_free(p7); + ossl_raise(ePKCS7Error, "No content in PKCS7"); + } RTYPEDDATA_DATA(self) = p7; PKCS7_free(p7_orig); @@ -355,6 +404,7 @@ ossl_pkcs7_initialize(int argc, VALUE *argv, VALUE self) return self; } +/* :nodoc: */ static VALUE ossl_pkcs7_copy(VALUE self, VALUE other) { @@ -368,7 +418,7 @@ ossl_pkcs7_copy(VALUE self, VALUE other) pkcs7 = PKCS7_dup(b); if (!pkcs7) { - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } DATA_PTR(self) = pkcs7; PKCS7_free(a); @@ -400,13 +450,13 @@ ossl_pkcs7_sym2typeid(VALUE sym) RSTRING_GETMEM(sym, s, l); for(i = 0; ; i++){ - if(i == numberof(p7_type_tab)) - ossl_raise(ePKCS7Error, "unknown type \"%"PRIsVALUE"\"", sym); - if(strlen(p7_type_tab[i].name) != l) continue; - if(strcmp(p7_type_tab[i].name, s) == 0){ - ret = p7_type_tab[i].nid; - break; - } + if(i == numberof(p7_type_tab)) + ossl_raise(ePKCS7Error, "unknown type \"%"PRIsVALUE"\"", sym); + if(strlen(p7_type_tab[i].name) != l) continue; + if(strcmp(p7_type_tab[i].name, s) == 0){ + ret = p7_type_tab[i].nid; + break; + } } return ret; @@ -423,7 +473,7 @@ ossl_pkcs7_set_type(VALUE self, VALUE type) GetPKCS7(self, p7); if(!PKCS7_set_type(p7, ossl_pkcs7_sym2typeid(type))) - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); return type; } @@ -439,15 +489,15 @@ ossl_pkcs7_get_type(VALUE self) GetPKCS7(self, p7); if(PKCS7_type_is_signed(p7)) - return ID2SYM(rb_intern("signed")); + return ID2SYM(rb_intern("signed")); if(PKCS7_type_is_encrypted(p7)) - return ID2SYM(rb_intern("encrypted")); + return ID2SYM(rb_intern("encrypted")); if(PKCS7_type_is_enveloped(p7)) - return ID2SYM(rb_intern("enveloped")); + return ID2SYM(rb_intern("enveloped")); if(PKCS7_type_is_signedAndEnveloped(p7)) - return ID2SYM(rb_intern("signedAndEnveloped")); + return ID2SYM(rb_intern("signedAndEnveloped")); if(PKCS7_type_is_data(p7)) - return ID2SYM(rb_intern("data")); + return ID2SYM(rb_intern("data")); return Qnil; } @@ -458,9 +508,9 @@ ossl_pkcs7_set_detached(VALUE self, VALUE flag) GetPKCS7(self, p7); if(flag != Qtrue && flag != Qfalse) - ossl_raise(ePKCS7Error, "must specify a boolean"); + ossl_raise(ePKCS7Error, "must specify a boolean"); if(!PKCS7_set_detached(p7, flag == Qtrue ? 1 : 0)) - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); return flag; } @@ -470,6 +520,8 @@ ossl_pkcs7_get_detached(VALUE self) { PKCS7 *p7; GetPKCS7(self, p7); + if (!PKCS7_type_is_signed(p7)) + return Qfalse; return PKCS7_get_detached(p7) ? Qtrue : Qfalse; } @@ -485,11 +537,14 @@ static VALUE ossl_pkcs7_set_cipher(VALUE self, VALUE cipher) { PKCS7 *pkcs7; + const EVP_CIPHER *ciph; + VALUE cipher_holder; GetPKCS7(self, pkcs7); - if (!PKCS7_set_cipher(pkcs7, ossl_evp_get_cipherbyname(cipher))) { - ossl_raise(ePKCS7Error, NULL); - } + ciph = ossl_evp_cipher_fetch(cipher, &cipher_holder); + if (!PKCS7_set_cipher(pkcs7, ciph)) + ossl_raise(ePKCS7Error, "PKCS7_set_cipher"); + rb_ivar_set(self, id_cipher_holder, cipher_holder); return cipher; } @@ -520,22 +575,17 @@ ossl_pkcs7_get_signer(VALUE self) { PKCS7 *pkcs7; STACK_OF(PKCS7_SIGNER_INFO) *sk; - PKCS7_SIGNER_INFO *si; int num, i; VALUE ary; GetPKCS7(self, pkcs7); - if (!(sk = PKCS7_get_signer_info(pkcs7))) { - OSSL_Debug("OpenSSL::PKCS7#get_signer_info == NULL!"); - return rb_ary_new(); - } - if ((num = sk_PKCS7_SIGNER_INFO_num(sk)) < 0) { - ossl_raise(ePKCS7Error, "Negative number of signers!"); - } - ary = rb_ary_new2(num); + if (!(sk = PKCS7_get_signer_info(pkcs7))) + return rb_ary_new(); + num = sk_PKCS7_SIGNER_INFO_num(sk); + ary = rb_ary_new_capa(num); for (i=0; i<num; i++) { - si = sk_PKCS7_SIGNER_INFO_value(sk, i); - rb_ary_push(ary, ossl_pkcs7si_new(si)); + PKCS7_SIGNER_INFO *si = sk_PKCS7_SIGNER_INFO_value(sk, i); + rb_ary_push(ary, ossl_pkcs7si_new(si)); } return ary; @@ -567,24 +617,21 @@ ossl_pkcs7_get_recipient(VALUE self) { PKCS7 *pkcs7; STACK_OF(PKCS7_RECIP_INFO) *sk; - PKCS7_RECIP_INFO *si; int num, i; VALUE ary; GetPKCS7(self, pkcs7); if (PKCS7_type_is_enveloped(pkcs7)) - sk = pkcs7->d.enveloped->recipientinfo; + sk = pkcs7->d.enveloped->recipientinfo; else if (PKCS7_type_is_signedAndEnveloped(pkcs7)) - sk = pkcs7->d.signed_and_enveloped->recipientinfo; + sk = pkcs7->d.signed_and_enveloped->recipientinfo; else sk = NULL; if (!sk) return rb_ary_new(); - if ((num = sk_PKCS7_RECIP_INFO_num(sk)) < 0) { - ossl_raise(ePKCS7Error, "Negative number of recipient!"); - } - ary = rb_ary_new2(num); + num = sk_PKCS7_RECIP_INFO_num(sk); + ary = rb_ary_new_capa(num); for (i=0; i<num; i++) { - si = sk_PKCS7_RECIP_INFO_value(sk, i); - rb_ary_push(ary, ossl_pkcs7ri_new(si)); + PKCS7_RECIP_INFO *ri = sk_PKCS7_RECIP_INFO_value(sk, i); + rb_ary_push(ary, ossl_pkcs7ri_new(ri)); } return ary; @@ -599,7 +646,7 @@ ossl_pkcs7_add_certificate(VALUE self, VALUE cert) GetPKCS7(self, pkcs7); x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */ if (!PKCS7_add_certificate(pkcs7, x509)){ - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } return self; @@ -615,13 +662,13 @@ pkcs7_get_certs(VALUE self) GetPKCS7(self, pkcs7); i = OBJ_obj2nid(pkcs7->type); switch(i){ - case NID_pkcs7_signed: + case NID_pkcs7_signed: certs = pkcs7->d.sign->cert; break; - case NID_pkcs7_signedAndEnveloped: + case NID_pkcs7_signedAndEnveloped: certs = pkcs7->d.signed_and_enveloped->cert; break; - default: + default: certs = NULL; } @@ -638,13 +685,13 @@ pkcs7_get_crls(VALUE self) GetPKCS7(self, pkcs7); i = OBJ_obj2nid(pkcs7->type); switch(i){ - case NID_pkcs7_signed: + case NID_pkcs7_signed: crls = pkcs7->d.sign->crl; break; - case NID_pkcs7_signedAndEnveloped: + case NID_pkcs7_signedAndEnveloped: crls = pkcs7->d.signed_and_enveloped->crl; break; - default: + default: crls = NULL; } @@ -664,7 +711,10 @@ ossl_pkcs7_set_certificates(VALUE self, VALUE ary) X509 *cert; certs = pkcs7_get_certs(self); - while((cert = sk_X509_pop(certs))) X509_free(cert); + if (certs) { + while ((cert = sk_X509_pop(certs))) + X509_free(cert); + } rb_block_call(ary, rb_intern("each"), 0, 0, ossl_pkcs7_set_certs_i, self); return ary; @@ -673,7 +723,10 @@ ossl_pkcs7_set_certificates(VALUE self, VALUE ary) static VALUE ossl_pkcs7_get_certificates(VALUE self) { - return ossl_x509_sk2ary(pkcs7_get_certs(self)); + STACK_OF(X509) *certs = pkcs7_get_certs(self); + if (!certs) + return Qnil; + return ossl_x509_sk2ary(certs); } static VALUE @@ -685,7 +738,7 @@ ossl_pkcs7_add_crl(VALUE self, VALUE crl) GetPKCS7(self, pkcs7); /* NO DUP needed! */ x509crl = GetX509CRLPtr(crl); if (!PKCS7_add_crl(pkcs7, x509crl)) { - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } return self; @@ -704,7 +757,10 @@ ossl_pkcs7_set_crls(VALUE self, VALUE ary) X509_CRL *crl; crls = pkcs7_get_crls(self); - while((crl = sk_X509_CRL_pop(crls))) X509_CRL_free(crl); + if (crls) { + while ((crl = sk_X509_CRL_pop(crls))) + X509_CRL_free(crl); + } rb_block_call(ary, rb_intern("each"), 0, 0, ossl_pkcs7_set_crls_i, self); return ary; @@ -713,7 +769,10 @@ ossl_pkcs7_set_crls(VALUE self, VALUE ary) static VALUE ossl_pkcs7_get_crls(VALUE self) { - return ossl_x509crl_sk2ary(pkcs7_get_crls(self)); + STACK_OF(X509_CRL) *crls = pkcs7_get_crls(self); + if (!crls) + return Qnil; + return ossl_x509crl_sk2ary(crls); } static VALUE @@ -726,7 +785,6 @@ ossl_pkcs7_verify(int argc, VALUE *argv, VALUE self) BIO *in, *out; PKCS7 *p7; VALUE data; - const char *msg; GetPKCS7(self, p7); rb_scan_args(argc, argv, "22", &certs, &store, &indata, &flags); @@ -736,28 +794,30 @@ ossl_pkcs7_verify(int argc, VALUE *argv, VALUE self) in = NIL_P(indata) ? NULL : ossl_obj2bio(&indata); if(NIL_P(certs)) x509s = NULL; else{ - x509s = ossl_protect_x509_ary2sk(certs, &status); - if(status){ - BIO_free(in); - rb_jump_tag(status); - } + x509s = ossl_protect_x509_ary2sk(certs, &status); + if(status){ + BIO_free(in); + rb_jump_tag(status); + } } if(!(out = BIO_new(BIO_s_mem()))){ - BIO_free(in); - sk_X509_pop_free(x509s, X509_free); - ossl_raise(ePKCS7Error, NULL); + BIO_free(in); + sk_X509_pop_free(x509s, X509_free); + ossl_raise(ePKCS7Error, NULL); } ok = PKCS7_verify(p7, x509s, x509st, in, out, flg); BIO_free(in); sk_X509_pop_free(x509s, X509_free); - if (ok < 0) ossl_raise(ePKCS7Error, "PKCS7_verify"); - msg = ERR_reason_error_string(ERR_peek_error()); - ossl_pkcs7_set_err_string(self, msg ? rb_str_new2(msg) : Qnil); - ossl_clear_error(); data = ossl_membio2str(out); ossl_pkcs7_set_data(self, data); - - return (ok == 1) ? Qtrue : Qfalse; + if (ok != 1) { + const char *msg = ERR_reason_error_string(ERR_peek_error()); + ossl_pkcs7_set_err_string(self, msg ? rb_str_new_cstr(msg) : Qnil); + ossl_clear_error(); + return Qfalse; + } + ossl_pkcs7_set_err_string(self, Qnil); + return Qtrue; } static VALUE @@ -777,10 +837,10 @@ ossl_pkcs7_decrypt(int argc, VALUE *argv, VALUE self) flg = NIL_P(flags) ? 0 : NUM2INT(flags); GetPKCS7(self, p7); if(!(out = BIO_new(BIO_s_mem()))) - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); if(!PKCS7_decrypt(p7, key, x509, out, flg)){ - BIO_free(out); - ossl_raise(ePKCS7Error, NULL); + BIO_free(out); + ossl_raise(ePKCS7Error, NULL); } str = ossl_membio2str(out); /* out will be free */ @@ -793,30 +853,38 @@ ossl_pkcs7_add_data(VALUE self, VALUE data) PKCS7 *pkcs7; BIO *out, *in; char buf[4096]; - int len; + int len, ret; GetPKCS7(self, pkcs7); - if(PKCS7_type_is_signed(pkcs7)){ - if(!PKCS7_content_new(pkcs7, NID_pkcs7_data)) - ossl_raise(ePKCS7Error, NULL); + if (PKCS7_type_is_signed(pkcs7)) { + if (!PKCS7_content_new(pkcs7, NID_pkcs7_data)) + ossl_raise(ePKCS7Error, "PKCS7_content_new"); } in = ossl_obj2bio(&data); - if(!(out = PKCS7_dataInit(pkcs7, NULL))) goto err; - for(;;){ - if((len = BIO_read(in, buf, sizeof(buf))) <= 0) - break; - if(BIO_write(out, buf, len) != len) - goto err; + if (!(out = PKCS7_dataInit(pkcs7, NULL))) { + BIO_free(in); + ossl_raise(ePKCS7Error, "PKCS7_dataInit"); } - if(!PKCS7_dataFinal(pkcs7, out)) goto err; - ossl_pkcs7_set_data(self, Qnil); - - err: + for (;;) { + if ((len = BIO_read(in, buf, sizeof(buf))) <= 0) + break; + if (BIO_write(out, buf, len) != len) { + BIO_free_all(out); + BIO_free(in); + ossl_raise(ePKCS7Error, "BIO_write"); + } + } + if (BIO_flush(out) <= 0) { + BIO_free_all(out); + BIO_free(in); + ossl_raise(ePKCS7Error, "BIO_flush"); + } + ret = PKCS7_dataFinal(pkcs7, out); BIO_free_all(out); BIO_free(in); - if(ERR_peek_error()){ - ossl_raise(ePKCS7Error, NULL); - } + if (!ret) + ossl_raise(ePKCS7Error, "PKCS7_dataFinal"); + ossl_pkcs7_set_data(self, Qnil); return data; } @@ -831,17 +899,36 @@ ossl_pkcs7_to_der(VALUE self) GetPKCS7(self, pkcs7); if((len = i2d_PKCS7(pkcs7, NULL)) <= 0) - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_PKCS7(pkcs7, &p) <= 0) - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); ossl_str_adjust(str, p); return str; } static VALUE +ossl_pkcs7_to_text(VALUE self) +{ + PKCS7 *pkcs7; + BIO *out; + VALUE str; + + GetPKCS7(self, pkcs7); + if(!(out = BIO_new(BIO_s_mem()))) + ossl_raise(ePKCS7Error, NULL); + if(!PKCS7_print_ctx(out, pkcs7, 0, NULL)) { + BIO_free(out); + ossl_raise(ePKCS7Error, NULL); + } + str = ossl_membio2str(out); + + return str; +} + +static VALUE ossl_pkcs7_to_pem(VALUE self) { PKCS7 *pkcs7; @@ -850,11 +937,11 @@ ossl_pkcs7_to_pem(VALUE self) GetPKCS7(self, pkcs7); if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } if (!PEM_write_bio_PKCS7(out, pkcs7)) { - BIO_free(out); - ossl_raise(ePKCS7Error, NULL); + BIO_free(out); + ossl_raise(ePKCS7Error, NULL); } str = ossl_membio2str(out); @@ -872,7 +959,7 @@ ossl_pkcs7si_alloc(VALUE klass) obj = NewPKCS7si(klass); if (!(p7si = PKCS7_SIGNER_INFO_new())) { - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } SetPKCS7si(obj, p7si); @@ -886,14 +973,15 @@ ossl_pkcs7si_initialize(VALUE self, VALUE cert, VALUE key, VALUE digest) EVP_PKEY *pkey; X509 *x509; const EVP_MD *md; + VALUE md_holder; pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */ - md = ossl_evp_get_digestbyname(digest); + md = ossl_evp_md_fetch(digest, &md_holder); GetPKCS7si(self, p7si); - if (!(PKCS7_SIGNER_INFO_set(p7si, x509, pkey, (EVP_MD*)md))) { - ossl_raise(ePKCS7Error, NULL); - } + if (!(PKCS7_SIGNER_INFO_set(p7si, x509, pkey, md))) + ossl_raise(ePKCS7Error, "PKCS7_SIGNER_INFO_set"); + rb_ivar_set(self, id_md_holder, md_holder); return self; } @@ -927,10 +1015,10 @@ ossl_pkcs7si_get_signed_time(VALUE self) GetPKCS7si(self, p7si); if (!(asn1obj = PKCS7_get_signed_attribute(p7si, NID_pkcs9_signingTime))) { - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } if (asn1obj->type == V_ASN1_UTCTIME) { - return asn1time_to_time(asn1obj->value.utctime); + return asn1time_to_time(asn1obj->value.utctime); } /* * OR @@ -952,7 +1040,7 @@ ossl_pkcs7ri_alloc(VALUE klass) obj = NewPKCS7ri(klass); if (!(p7ri = PKCS7_RECIP_INFO_new())) { - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } SetPKCS7ri(obj, p7ri); @@ -968,7 +1056,7 @@ ossl_pkcs7ri_initialize(VALUE self, VALUE cert) x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */ GetPKCS7ri(self, p7ri); if (!PKCS7_RECIP_INFO_set(p7ri, x509)) { - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } return self; @@ -1011,11 +1099,6 @@ void Init_ossl_pkcs7(void) { #undef rb_intern -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - cPKCS7 = rb_define_class_under(mOSSL, "PKCS7", rb_cObject); ePKCS7Error = rb_define_class_under(cPKCS7, "PKCS7Error", eOSSLError); rb_define_singleton_method(cPKCS7, "read_smime", ossl_pkcs7_s_read_smime, 1); @@ -1050,6 +1133,7 @@ Init_ossl_pkcs7(void) rb_define_method(cPKCS7, "to_pem", ossl_pkcs7_to_pem, 0); rb_define_alias(cPKCS7, "to_s", "to_pem"); rb_define_method(cPKCS7, "to_der", ossl_pkcs7_to_der, 0); + rb_define_method(cPKCS7, "to_text", ossl_pkcs7_to_text, 0); cPKCS7Signer = rb_define_class_under(cPKCS7, "SignerInfo", rb_cObject); rb_define_const(cPKCS7, "Signer", cPKCS7Signer); @@ -1078,4 +1162,7 @@ Init_ossl_pkcs7(void) DefPKCS7Const(BINARY); DefPKCS7Const(NOATTR); DefPKCS7Const(NOSMIMECAP); + + id_md_holder = rb_intern_const("EVP_MD_holder"); + id_cipher_holder = rb_intern_const("EVP_CIPHER_holder"); } diff --git a/ext/openssl/ossl_pkcs7.h b/ext/openssl/ossl_pkcs7.h index 3e1b094670..140fda1835 100644 --- a/ext/openssl/ossl_pkcs7.h +++ b/ext/openssl/ossl_pkcs7.h @@ -5,32 +5,12 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(_OSSL_PKCS7_H_) #define _OSSL_PKCS7_H_ -#define NewPKCS7(klass) \ - TypedData_Wrap_Struct((klass), &ossl_pkcs7_type, 0) -#define SetPKCS7(obj, pkcs7) do { \ - if (!(pkcs7)) { \ - ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \ - } \ - RTYPEDDATA_DATA(obj) = (pkcs7); \ -} while (0) -#define GetPKCS7(obj, pkcs7) do { \ - TypedData_Get_Struct((obj), PKCS7, &ossl_pkcs7_type, (pkcs7)); \ - if (!(pkcs7)) { \ - ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \ - } \ -} while (0) - -extern const rb_data_type_t ossl_pkcs7_type; -extern VALUE cPKCS7; -extern VALUE cPKCS7Signer; -extern VALUE cPKCS7Recipient; -extern VALUE ePKCS7Error; - +VALUE ossl_pkcs7_new(PKCS7 *p7); void Init_ossl_pkcs7(void); #endif /* _OSSL_PKCS7_H_ */ diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 013412c27f..d2fd5b29c3 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -33,13 +33,13 @@ ossl_evp_pkey_free(void *ptr) const rb_data_type_t ossl_evp_pkey_type = { "OpenSSL/EVP_PKEY", { - 0, ossl_evp_pkey_free, + 0, ossl_evp_pkey_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; static VALUE -pkey_new0(VALUE arg) +pkey_wrap0(VALUE arg) { EVP_PKEY *pkey = (EVP_PKEY *)arg; VALUE klass, obj; @@ -65,15 +65,15 @@ pkey_new0(VALUE arg) } VALUE -ossl_pkey_new(EVP_PKEY *pkey) +ossl_pkey_wrap(EVP_PKEY *pkey) { VALUE obj; int status; - obj = rb_protect(pkey_new0, (VALUE)pkey, &status); + obj = rb_protect(pkey_wrap0, (VALUE)pkey, &status); if (status) { - EVP_PKEY_free(pkey); - rb_jump_tag(status); + EVP_PKEY_free(pkey); + rb_jump_tag(status); } return obj; @@ -94,7 +94,8 @@ ossl_pkey_read(BIO *bio, const char *input_type, int selection, VALUE pass) selection, NULL, NULL); if (!dctx) goto out; - if (OSSL_DECODER_CTX_set_pem_password_cb(dctx, ossl_pem_passwd_cb, + if (selection == EVP_PKEY_KEYPAIR && + OSSL_DECODER_CTX_set_pem_password_cb(dctx, ossl_pem_passwd_cb, ppass) != 1) goto out; while (1) { @@ -187,23 +188,23 @@ ossl_pkey_read_generic(BIO *bio, VALUE pass) EVP_PKEY *pkey; if ((pkey = d2i_PrivateKey_bio(bio, NULL))) - goto out; + goto out; OSSL_BIO_reset(bio); if ((pkey = d2i_PKCS8PrivateKey_bio(bio, NULL, ossl_pem_passwd_cb, ppass))) - goto out; + goto out; OSSL_BIO_reset(bio); if ((pkey = d2i_PUBKEY_bio(bio, NULL))) - goto out; + goto out; OSSL_BIO_reset(bio); /* PEM_read_bio_PrivateKey() also parses PKCS #8 formats */ if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, ppass))) - goto out; + goto out; OSSL_BIO_reset(bio); if ((pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL))) - goto out; + goto out; OSSL_BIO_reset(bio); if ((pkey = PEM_read_bio_Parameters(bio, NULL))) - goto out; + goto out; out: return pkey; @@ -238,8 +239,8 @@ ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self) pkey = ossl_pkey_read_generic(bio, ossl_pem_passwd_value(pass)); BIO_free(bio); if (!pkey) - ossl_raise(ePKeyError, "Could not parse PKey"); - return ossl_pkey_new(pkey); + ossl_raise(ePKeyError, "Could not parse PKey"); + return ossl_pkey_wrap(pkey); } static VALUE @@ -443,7 +444,7 @@ pkey_generate(int argc, VALUE *argv, VALUE self, int genparam) } } - return ossl_pkey_new(gen_arg.pkey); + return ossl_pkey_wrap(gen_arg.pkey); } /* @@ -507,7 +508,7 @@ ossl_pkey_s_generate_key(int argc, VALUE *argv, VALUE self) void ossl_pkey_check_public_key(const EVP_PKEY *pkey) { -#if OSSL_OPENSSL_PREREQ(3, 0, 0) +#ifdef OSSL_HAVE_IMMUTABLE_PKEY if (EVP_PKEY_missing_parameters(pkey)) ossl_raise(ePKeyError, "parameters missing"); #else @@ -515,35 +516,34 @@ ossl_pkey_check_public_key(const EVP_PKEY *pkey) const BIGNUM *n, *e, *pubkey; if (EVP_PKEY_missing_parameters(pkey)) - ossl_raise(ePKeyError, "parameters missing"); + ossl_raise(ePKeyError, "parameters missing"); - /* OpenSSL < 1.1.0 takes non-const pointer */ - ptr = EVP_PKEY_get0((EVP_PKEY *)pkey); + ptr = EVP_PKEY_get0(pkey); switch (EVP_PKEY_base_id(pkey)) { case EVP_PKEY_RSA: - RSA_get0_key(ptr, &n, &e, NULL); - if (n && e) - return; - break; + RSA_get0_key(ptr, &n, &e, NULL); + if (n && e) + return; + break; case EVP_PKEY_DSA: - DSA_get0_key(ptr, &pubkey, NULL); - if (pubkey) - return; - break; + DSA_get0_key(ptr, &pubkey, NULL); + if (pubkey) + return; + break; case EVP_PKEY_DH: - DH_get0_key(ptr, &pubkey, NULL); - if (pubkey) - return; - break; + DH_get0_key(ptr, &pubkey, NULL); + if (pubkey) + return; + break; #if !defined(OPENSSL_NO_EC) case EVP_PKEY_EC: - if (EC_KEY_get0_public_key(ptr)) - return; - break; + if (EC_KEY_get0_public_key(ptr)) + return; + break; #endif default: - /* unsupported type; assuming ok */ - return; + /* unsupported type; assuming ok */ + return; } ossl_raise(ePKeyError, "public key missing"); #endif @@ -610,12 +610,13 @@ static VALUE ossl_pkey_initialize(VALUE self) { if (rb_obj_is_instance_of(self, cPKey)) { - ossl_raise(rb_eTypeError, "OpenSSL::PKey::PKey can't be instantiated directly"); + ossl_raise(rb_eTypeError, "OpenSSL::PKey::PKey can't be instantiated directly"); } return self; } #ifdef HAVE_EVP_PKEY_DUP +/* :nodoc: */ static VALUE ossl_pkey_initialize_copy(VALUE self, VALUE other) { @@ -635,7 +636,29 @@ ossl_pkey_initialize_copy(VALUE self, VALUE other) } #endif -#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY +#ifndef OSSL_USE_PROVIDER +static int +lookup_pkey_type(VALUE type) +{ + const EVP_PKEY_ASN1_METHOD *ameth; + int pkey_id; + + StringValue(type); + /* + * XXX: EVP_PKEY_asn1_find_str() looks up a PEM type string. Should we use + * OBJ_txt2nid() instead (and then somehow check if the NID is an acceptable + * EVP_PKEY type)? + * It is probably fine, though, since it can handle all algorithms that + * support raw keys in 1.1.1: { X25519, X448, ED25519, ED448, HMAC }. + */ + ameth = EVP_PKEY_asn1_find_str(NULL, RSTRING_PTR(type), RSTRING_LENINT(type)); + if (!ameth) + ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", type); + EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth); + return pkey_id; +} +#endif + /* * call-seq: * OpenSSL::PKey.new_raw_private_key(algo, string) -> PKey @@ -647,28 +670,27 @@ static VALUE ossl_pkey_new_raw_private_key(VALUE self, VALUE type, VALUE key) { EVP_PKEY *pkey; - const EVP_PKEY_ASN1_METHOD *ameth; - int pkey_id; size_t keylen; - StringValue(type); StringValue(key); - ameth = EVP_PKEY_asn1_find_str(NULL, RSTRING_PTR(type), RSTRING_LENINT(type)); - if (!ameth) - ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", type); - EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth); - keylen = RSTRING_LEN(key); +#ifdef OSSL_USE_PROVIDER + pkey = EVP_PKEY_new_raw_private_key_ex(NULL, StringValueCStr(type), NULL, + (unsigned char *)RSTRING_PTR(key), + keylen); + if (!pkey) + ossl_raise(ePKeyError, "EVP_PKEY_new_raw_private_key_ex"); +#else + int pkey_id = lookup_pkey_type(type); pkey = EVP_PKEY_new_raw_private_key(pkey_id, NULL, (unsigned char *)RSTRING_PTR(key), keylen); if (!pkey) ossl_raise(ePKeyError, "EVP_PKEY_new_raw_private_key"); +#endif - return ossl_pkey_new(pkey); + return ossl_pkey_wrap(pkey); } -#endif -#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY /* * call-seq: * OpenSSL::PKey.new_raw_public_key(algo, string) -> PKey @@ -680,26 +702,26 @@ static VALUE ossl_pkey_new_raw_public_key(VALUE self, VALUE type, VALUE key) { EVP_PKEY *pkey; - const EVP_PKEY_ASN1_METHOD *ameth; - int pkey_id; size_t keylen; - StringValue(type); StringValue(key); - ameth = EVP_PKEY_asn1_find_str(NULL, RSTRING_PTR(type), RSTRING_LENINT(type)); - if (!ameth) - ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", type); - EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth); - keylen = RSTRING_LEN(key); +#ifdef OSSL_USE_PROVIDER + pkey = EVP_PKEY_new_raw_public_key_ex(NULL, StringValueCStr(type), NULL, + (unsigned char *)RSTRING_PTR(key), + keylen); + if (!pkey) + ossl_raise(ePKeyError, "EVP_PKEY_new_raw_public_key_ex"); +#else + int pkey_id = lookup_pkey_type(type); pkey = EVP_PKEY_new_raw_public_key(pkey_id, NULL, (unsigned char *)RSTRING_PTR(key), keylen); if (!pkey) ossl_raise(ePKeyError, "EVP_PKEY_new_raw_public_key"); +#endif - return ossl_pkey_new(pkey); + return ossl_pkey_wrap(pkey); } -#endif /* * call-seq: @@ -715,6 +737,10 @@ ossl_pkey_oid(VALUE self) GetPKey(self, pkey); nid = EVP_PKEY_id(pkey); +#ifdef OSSL_USE_PROVIDER + if (nid == EVP_PKEY_KEYMGMT) + ossl_raise(ePKeyError, "EVP_PKEY_id"); +#endif return rb_str_new_cstr(OBJ_nid2sn(nid)); } @@ -728,13 +754,23 @@ static VALUE ossl_pkey_inspect(VALUE self) { EVP_PKEY *pkey; - int nid; GetPKey(self, pkey); - nid = EVP_PKEY_id(pkey); - return rb_sprintf("#<%"PRIsVALUE":%p oid=%s>", - rb_class_name(CLASS_OF(self)), (void *)self, - OBJ_nid2sn(nid)); + VALUE str = rb_sprintf("#<%"PRIsVALUE":%p", + rb_obj_class(self), (void *)self); + int nid = EVP_PKEY_id(pkey); +#ifdef OSSL_USE_PROVIDER + if (nid != EVP_PKEY_KEYMGMT) +#endif + rb_str_catf(str, " oid=%s", OBJ_nid2sn(nid)); +#ifdef OSSL_USE_PROVIDER + rb_str_catf(str, " type_name=%s", EVP_PKEY_get0_type_name(pkey)); + const OSSL_PROVIDER *prov = EVP_PKEY_get0_provider(pkey); + if (prov) + rb_str_catf(str, " provider=%s", OSSL_PROVIDER_get0_name(prov)); +#endif + rb_str_catf(str, ">"); + return str; } /* @@ -778,44 +814,33 @@ VALUE ossl_pkey_export_traditional(int argc, VALUE *argv, VALUE self, int to_der) { EVP_PKEY *pkey; - VALUE cipher, pass; + VALUE cipher, pass, cipher_holder; const EVP_CIPHER *enc = NULL; BIO *bio; GetPKey(self, pkey); rb_scan_args(argc, argv, "02", &cipher, &pass); if (!NIL_P(cipher)) { - enc = ossl_evp_get_cipherbyname(cipher); - pass = ossl_pem_passwd_value(pass); + enc = ossl_evp_cipher_fetch(cipher, &cipher_holder); + pass = ossl_pem_passwd_value(pass); } bio = BIO_new(BIO_s_mem()); if (!bio) - ossl_raise(ePKeyError, "BIO_new"); + ossl_raise(ePKeyError, "BIO_new"); if (to_der) { - if (!i2d_PrivateKey_bio(bio, pkey)) { - BIO_free(bio); - ossl_raise(ePKeyError, "i2d_PrivateKey_bio"); - } + if (!i2d_PrivateKey_bio(bio, pkey)) { + BIO_free(bio); + ossl_raise(ePKeyError, "i2d_PrivateKey_bio"); + } } else { -#if OSSL_OPENSSL_PREREQ(1, 1, 0) || OSSL_LIBRESSL_PREREQ(3, 5, 0) - if (!PEM_write_bio_PrivateKey_traditional(bio, pkey, enc, NULL, 0, - ossl_pem_passwd_cb, - (void *)pass)) { -#else - char pem_str[80]; - const char *aname; - - EVP_PKEY_asn1_get0_info(NULL, NULL, NULL, NULL, &aname, pkey->ameth); - snprintf(pem_str, sizeof(pem_str), "%s PRIVATE KEY", aname); - if (!PEM_ASN1_write_bio((i2d_of_void *)i2d_PrivateKey, pem_str, bio, - pkey, enc, NULL, 0, ossl_pem_passwd_cb, - (void *)pass)) { -#endif - BIO_free(bio); - ossl_raise(ePKeyError, "PEM_write_bio_PrivateKey_traditional"); - } + if (!PEM_write_bio_PrivateKey_traditional(bio, pkey, enc, NULL, 0, + ossl_pem_passwd_cb, + (void *)pass)) { + BIO_free(bio); + ossl_raise(ePKeyError, "PEM_write_bio_PrivateKey_traditional"); + } } return ossl_membio2str(bio); } @@ -824,37 +849,37 @@ static VALUE do_pkcs8_export(int argc, VALUE *argv, VALUE self, int to_der) { EVP_PKEY *pkey; - VALUE cipher, pass; + VALUE cipher, pass, cipher_holder; const EVP_CIPHER *enc = NULL; BIO *bio; GetPKey(self, pkey); rb_scan_args(argc, argv, "02", &cipher, &pass); if (argc > 0) { - /* - * TODO: EncryptedPrivateKeyInfo actually has more options. - * Should they be exposed? - */ - enc = ossl_evp_get_cipherbyname(cipher); - pass = ossl_pem_passwd_value(pass); + /* + * TODO: EncryptedPrivateKeyInfo actually has more options. + * Should they be exposed? + */ + enc = ossl_evp_cipher_fetch(cipher, &cipher_holder); + pass = ossl_pem_passwd_value(pass); } bio = BIO_new(BIO_s_mem()); if (!bio) - ossl_raise(ePKeyError, "BIO_new"); + ossl_raise(ePKeyError, "BIO_new"); if (to_der) { - if (!i2d_PKCS8PrivateKey_bio(bio, pkey, enc, NULL, 0, - ossl_pem_passwd_cb, (void *)pass)) { - BIO_free(bio); - ossl_raise(ePKeyError, "i2d_PKCS8PrivateKey_bio"); - } + if (!i2d_PKCS8PrivateKey_bio(bio, pkey, enc, NULL, 0, + ossl_pem_passwd_cb, (void *)pass)) { + BIO_free(bio); + ossl_raise(ePKeyError, "i2d_PKCS8PrivateKey_bio"); + } } else { - if (!PEM_write_bio_PKCS8PrivateKey(bio, pkey, enc, NULL, 0, - ossl_pem_passwd_cb, (void *)pass)) { - BIO_free(bio); - ossl_raise(ePKeyError, "PEM_write_bio_PKCS8PrivateKey"); - } + if (!PEM_write_bio_PKCS8PrivateKey(bio, pkey, enc, NULL, 0, + ossl_pem_passwd_cb, (void *)pass)) { + BIO_free(bio); + ossl_raise(ePKeyError, "PEM_write_bio_PKCS8PrivateKey"); + } } return ossl_membio2str(bio); } @@ -901,7 +926,6 @@ ossl_pkey_private_to_pem(int argc, VALUE *argv, VALUE self) return do_pkcs8_export(argc, argv, self, 0); } -#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY /* * call-seq: * pkey.raw_private_key => string @@ -928,7 +952,6 @@ ossl_pkey_raw_private_key(VALUE self) return str; } -#endif VALUE ossl_pkey_export_spki(VALUE self, int to_der) @@ -937,20 +960,21 @@ ossl_pkey_export_spki(VALUE self, int to_der) BIO *bio; GetPKey(self, pkey); + ossl_pkey_check_public_key(pkey); bio = BIO_new(BIO_s_mem()); if (!bio) - ossl_raise(ePKeyError, "BIO_new"); + ossl_raise(ePKeyError, "BIO_new"); if (to_der) { - if (!i2d_PUBKEY_bio(bio, pkey)) { - BIO_free(bio); - ossl_raise(ePKeyError, "i2d_PUBKEY_bio"); - } + if (!i2d_PUBKEY_bio(bio, pkey)) { + BIO_free(bio); + ossl_raise(ePKeyError, "i2d_PUBKEY_bio"); + } } else { - if (!PEM_write_bio_PUBKEY(bio, pkey)) { - BIO_free(bio); - ossl_raise(ePKeyError, "PEM_write_bio_PUBKEY"); - } + if (!PEM_write_bio_PUBKEY(bio, pkey)) { + BIO_free(bio); + ossl_raise(ePKeyError, "PEM_write_bio_PUBKEY"); + } } return ossl_membio2str(bio); } @@ -985,7 +1009,6 @@ ossl_pkey_public_to_pem(VALUE self) return ossl_pkey_export_spki(self, 0); } -#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY /* * call-seq: * pkey.raw_public_key => string @@ -1012,7 +1035,6 @@ ossl_pkey_raw_public_key(VALUE self) return str; } -#endif /* * call-seq: @@ -1089,7 +1111,7 @@ static VALUE ossl_pkey_sign(int argc, VALUE *argv, VALUE self) { EVP_PKEY *pkey; - VALUE digest, data, options, sig; + VALUE digest, data, options, sig, md_holder; const EVP_MD *md = NULL; EVP_MD_CTX *ctx; EVP_PKEY_CTX *pctx; @@ -1099,7 +1121,7 @@ ossl_pkey_sign(int argc, VALUE *argv, VALUE self) pkey = GetPrivPKeyPtr(self); rb_scan_args(argc, argv, "21", &digest, &data, &options); if (!NIL_P(digest)) - md = ossl_evp_get_digestbyname(digest); + md = ossl_evp_md_fetch(digest, &md_holder); StringValue(data); ctx = EVP_MD_CTX_new(); @@ -1116,7 +1138,6 @@ ossl_pkey_sign(int argc, VALUE *argv, VALUE self) rb_jump_tag(state); } } -#if OSSL_OPENSSL_PREREQ(1, 1, 1) || OSSL_LIBRESSL_PREREQ(3, 4, 0) if (EVP_DigestSign(ctx, NULL, &siglen, (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data)) < 1) { EVP_MD_CTX_free(ctx); @@ -1137,30 +1158,6 @@ ossl_pkey_sign(int argc, VALUE *argv, VALUE self) EVP_MD_CTX_free(ctx); ossl_raise(ePKeyError, "EVP_DigestSign"); } -#else - if (EVP_DigestSignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) < 1) { - EVP_MD_CTX_free(ctx); - ossl_raise(ePKeyError, "EVP_DigestSignUpdate"); - } - if (EVP_DigestSignFinal(ctx, NULL, &siglen) < 1) { - EVP_MD_CTX_free(ctx); - ossl_raise(ePKeyError, "EVP_DigestSignFinal"); - } - if (siglen > LONG_MAX) { - EVP_MD_CTX_free(ctx); - rb_raise(ePKeyError, "signature would be too large"); - } - sig = ossl_str_new(NULL, (long)siglen, &state); - if (state) { - EVP_MD_CTX_free(ctx); - rb_jump_tag(state); - } - if (EVP_DigestSignFinal(ctx, (unsigned char *)RSTRING_PTR(sig), - &siglen) < 1) { - EVP_MD_CTX_free(ctx); - ossl_raise(ePKeyError, "EVP_DigestSignFinal"); - } -#endif EVP_MD_CTX_free(ctx); rb_str_set_len(sig, siglen); return sig; @@ -1193,7 +1190,7 @@ static VALUE ossl_pkey_verify(int argc, VALUE *argv, VALUE self) { EVP_PKEY *pkey; - VALUE digest, sig, data, options; + VALUE digest, sig, data, options, md_holder; const EVP_MD *md = NULL; EVP_MD_CTX *ctx; EVP_PKEY_CTX *pctx; @@ -1203,7 +1200,7 @@ ossl_pkey_verify(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "31", &digest, &sig, &data, &options); ossl_pkey_check_public_key(pkey); if (!NIL_P(digest)) - md = ossl_evp_get_digestbyname(digest); + md = ossl_evp_md_fetch(digest, &md_holder); StringValue(sig); StringValue(data); @@ -1221,24 +1218,12 @@ ossl_pkey_verify(int argc, VALUE *argv, VALUE self) rb_jump_tag(state); } } -#if OSSL_OPENSSL_PREREQ(1, 1, 1) || OSSL_LIBRESSL_PREREQ(3, 4, 0) ret = EVP_DigestVerify(ctx, (unsigned char *)RSTRING_PTR(sig), RSTRING_LEN(sig), (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data)); EVP_MD_CTX_free(ctx); if (ret < 0) ossl_raise(ePKeyError, "EVP_DigestVerify"); -#else - if (EVP_DigestVerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) < 1) { - EVP_MD_CTX_free(ctx); - ossl_raise(ePKeyError, "EVP_DigestVerifyUpdate"); - } - ret = EVP_DigestVerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig), - RSTRING_LEN(sig)); - EVP_MD_CTX_free(ctx); - if (ret < 0) - ossl_raise(ePKeyError, "EVP_DigestVerifyFinal"); -#endif if (ret) return Qtrue; else { @@ -1284,7 +1269,7 @@ static VALUE ossl_pkey_sign_raw(int argc, VALUE *argv, VALUE self) { EVP_PKEY *pkey; - VALUE digest, data, options, sig; + VALUE digest, data, options, sig, md_holder; const EVP_MD *md = NULL; EVP_PKEY_CTX *ctx; size_t outlen; @@ -1293,7 +1278,7 @@ ossl_pkey_sign_raw(int argc, VALUE *argv, VALUE self) GetPKey(self, pkey); rb_scan_args(argc, argv, "21", &digest, &data, &options); if (!NIL_P(digest)) - md = ossl_evp_get_digestbyname(digest); + md = ossl_evp_md_fetch(digest, &md_holder); StringValue(data); ctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL); @@ -1360,7 +1345,7 @@ static VALUE ossl_pkey_verify_raw(int argc, VALUE *argv, VALUE self) { EVP_PKEY *pkey; - VALUE digest, sig, data, options; + VALUE digest, sig, data, options, md_holder; const EVP_MD *md = NULL; EVP_PKEY_CTX *ctx; int state, ret; @@ -1369,7 +1354,7 @@ ossl_pkey_verify_raw(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "31", &digest, &sig, &data, &options); ossl_pkey_check_public_key(pkey); if (!NIL_P(digest)) - md = ossl_evp_get_digestbyname(digest); + md = ossl_evp_md_fetch(digest, &md_holder); StringValue(sig); StringValue(data); @@ -1423,7 +1408,7 @@ static VALUE ossl_pkey_verify_recover(int argc, VALUE *argv, VALUE self) { EVP_PKEY *pkey; - VALUE digest, sig, options, out; + VALUE digest, sig, options, out, md_holder; const EVP_MD *md = NULL; EVP_PKEY_CTX *ctx; int state; @@ -1433,7 +1418,7 @@ ossl_pkey_verify_recover(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "21", &digest, &sig, &options); ossl_pkey_check_public_key(pkey); if (!NIL_P(digest)) - md = ossl_evp_get_digestbyname(digest); + md = ossl_evp_md_fetch(digest, &md_holder); StringValue(sig); ctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL); @@ -1673,11 +1658,6 @@ void Init_ossl_pkey(void) { #undef rb_intern -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - /* Document-module: OpenSSL::PKey * * == Asymmetric Public Key Algorithms @@ -1733,7 +1713,16 @@ Init_ossl_pkey(void) /* Document-class: OpenSSL::PKey::PKeyError * - *Raised when errors occur during PKey#sign or PKey#verify. + * Raised when errors occur during PKey#sign or PKey#verify. + * + * Before version 4.0.0, OpenSSL::PKey::PKeyError had the following + * subclasses. These subclasses have been removed and the constants are + * now defined as aliases of OpenSSL::PKey::PKeyError. + * + * * OpenSSL::PKey::DHError + * * OpenSSL::PKey::DSAError + * * OpenSSL::PKey::ECError + * * OpenSSL::PKey::RSAError */ ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); @@ -1751,10 +1740,8 @@ Init_ossl_pkey(void) rb_define_module_function(mPKey, "read", ossl_pkey_new_from_data, -1); rb_define_module_function(mPKey, "generate_parameters", ossl_pkey_s_generate_parameters, -1); rb_define_module_function(mPKey, "generate_key", ossl_pkey_s_generate_key, -1); -#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY rb_define_module_function(mPKey, "new_raw_private_key", ossl_pkey_new_raw_private_key, 2); rb_define_module_function(mPKey, "new_raw_public_key", ossl_pkey_new_raw_public_key, 2); -#endif rb_define_alloc_func(cPKey, ossl_pkey_alloc); rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0); @@ -1770,10 +1757,8 @@ Init_ossl_pkey(void) rb_define_method(cPKey, "private_to_pem", ossl_pkey_private_to_pem, -1); rb_define_method(cPKey, "public_to_der", ossl_pkey_public_to_der, 0); rb_define_method(cPKey, "public_to_pem", ossl_pkey_public_to_pem, 0); -#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY rb_define_method(cPKey, "raw_private_key", ossl_pkey_raw_private_key, 0); rb_define_method(cPKey, "raw_public_key", ossl_pkey_raw_public_key, 0); -#endif rb_define_method(cPKey, "compare?", ossl_pkey_compare, 1); rb_define_method(cPKey, "sign", ossl_pkey_sign, -1); diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h index 10669b824c..023361b90f 100644 --- a/ext/openssl/ossl_pkey.h +++ b/ext/openssl/ossl_pkey.h @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(OSSL_PKEY_H) #define OSSL_PKEY_H @@ -22,12 +22,12 @@ extern const rb_data_type_t ossl_evp_pkey_type; #define GetPKey(obj, pkey) do {\ TypedData_Get_Struct((obj), EVP_PKEY, &ossl_evp_pkey_type, (pkey)); \ if (!(pkey)) { \ - rb_raise(rb_eRuntimeError, "PKEY wasn't initialized!");\ + rb_raise(rb_eRuntimeError, "PKEY wasn't initialized!");\ } \ } while (0) /* Takes ownership of the EVP_PKEY */ -VALUE ossl_pkey_new(EVP_PKEY *); +VALUE ossl_pkey_wrap(EVP_PKEY *); void ossl_pkey_check_public_key(const EVP_PKEY *); EVP_PKEY *ossl_pkey_read_generic(BIO *, VALUE); EVP_PKEY *GetPKeyPtr(VALUE); @@ -45,7 +45,7 @@ VALUE ossl_pkey_export_spki(VALUE self, int to_der); * #to_der. */ VALUE ossl_pkey_export_traditional(int argc, VALUE *argv, VALUE self, - int to_der); + int to_der); void Init_ossl_pkey(void); @@ -53,152 +53,141 @@ void Init_ossl_pkey(void); * RSA */ extern VALUE cRSA; -extern VALUE eRSAError; - void Init_ossl_rsa(void); /* * DSA */ extern VALUE cDSA; -extern VALUE eDSAError; - void Init_ossl_dsa(void); /* * DH */ extern VALUE cDH; -extern VALUE eDHError; - void Init_ossl_dh(void); /* * EC */ extern VALUE cEC; -extern VALUE eECError; -extern VALUE cEC_GROUP; -extern VALUE eEC_GROUP; -extern VALUE cEC_POINT; -extern VALUE eEC_POINT; VALUE ossl_ec_new(EVP_PKEY *); void Init_ossl_ec(void); -#define OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, _name, _get) \ -/* \ - * call-seq: \ - * _keytype##.##_name -> aBN \ - */ \ -static VALUE ossl_##_keytype##_get_##_name(VALUE self) \ -{ \ - const _type *obj; \ - const BIGNUM *bn; \ - \ - Get##_type(self, obj); \ - _get; \ - if (bn == NULL) \ - return Qnil; \ - return ossl_bn_new(bn); \ +#define OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, _name, _get) \ +/* \ + * call-seq: \ + * _keytype##.##_name -> aBN \ + */ \ +static VALUE ossl_##_keytype##_get_##_name(VALUE self) \ +{ \ + const _type *obj; \ + const BIGNUM *bn; \ + \ + Get##_type(self, obj); \ + _get; \ + if (bn == NULL) \ + return Qnil; \ + return ossl_bn_new(bn); \ } -#define OSSL_PKEY_BN_DEF_GETTER3(_keytype, _type, _group, a1, a2, a3) \ - OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a1, \ - _type##_get0_##_group(obj, &bn, NULL, NULL)) \ - OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a2, \ - _type##_get0_##_group(obj, NULL, &bn, NULL)) \ - OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a3, \ - _type##_get0_##_group(obj, NULL, NULL, &bn)) - -#define OSSL_PKEY_BN_DEF_GETTER2(_keytype, _type, _group, a1, a2) \ - OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a1, \ - _type##_get0_##_group(obj, &bn, NULL)) \ - OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a2, \ - _type##_get0_##_group(obj, NULL, &bn)) - -#if !OSSL_OPENSSL_PREREQ(3, 0, 0) -#define OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) \ -/* \ - * call-seq: \ - * _keytype##.set_##_group(a1, a2, a3) -> self \ - */ \ +#define OSSL_PKEY_BN_DEF_GETTER3(_keytype, _type, _group, a1, a2, a3) \ + OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a1, \ + _type##_get0_##_group(obj, &bn, NULL, NULL)) \ + OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a2, \ + _type##_get0_##_group(obj, NULL, &bn, NULL)) \ + OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a3, \ + _type##_get0_##_group(obj, NULL, NULL, &bn)) + +#define OSSL_PKEY_BN_DEF_GETTER2(_keytype, _type, _group, a1, a2) \ + OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a1, \ + _type##_get0_##_group(obj, &bn, NULL)) \ + OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a2, \ + _type##_get0_##_group(obj, NULL, &bn)) + +#ifndef OSSL_HAVE_IMMUTABLE_PKEY +#define OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) \ +/* \ + * call-seq: \ + * _keytype##.set_##_group(a1, a2, a3) -> self \ + */ \ static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2, VALUE v3) \ -{ \ - _type *obj; \ - BIGNUM *bn1 = NULL, *orig_bn1 = NIL_P(v1) ? NULL : GetBNPtr(v1);\ - BIGNUM *bn2 = NULL, *orig_bn2 = NIL_P(v2) ? NULL : GetBNPtr(v2);\ - BIGNUM *bn3 = NULL, *orig_bn3 = NIL_P(v3) ? NULL : GetBNPtr(v3);\ - \ - Get##_type(self, obj); \ - if ((orig_bn1 && !(bn1 = BN_dup(orig_bn1))) || \ - (orig_bn2 && !(bn2 = BN_dup(orig_bn2))) || \ - (orig_bn3 && !(bn3 = BN_dup(orig_bn3)))) { \ - BN_clear_free(bn1); \ - BN_clear_free(bn2); \ - BN_clear_free(bn3); \ - ossl_raise(eBNError, NULL); \ - } \ - \ - if (!_type##_set0_##_group(obj, bn1, bn2, bn3)) { \ - BN_clear_free(bn1); \ - BN_clear_free(bn2); \ - BN_clear_free(bn3); \ - ossl_raise(ePKeyError, #_type"_set0_"#_group); \ - } \ - return self; \ +{ \ + _type *obj; \ + BIGNUM *bn1 = NULL, *orig_bn1 = NIL_P(v1) ? NULL : GetBNPtr(v1);\ + BIGNUM *bn2 = NULL, *orig_bn2 = NIL_P(v2) ? NULL : GetBNPtr(v2);\ + BIGNUM *bn3 = NULL, *orig_bn3 = NIL_P(v3) ? NULL : GetBNPtr(v3);\ + \ + Get##_type(self, obj); \ + if ((orig_bn1 && !(bn1 = BN_dup(orig_bn1))) || \ + (orig_bn2 && !(bn2 = BN_dup(orig_bn2))) || \ + (orig_bn3 && !(bn3 = BN_dup(orig_bn3)))) { \ + BN_clear_free(bn1); \ + BN_clear_free(bn2); \ + BN_clear_free(bn3); \ + ossl_raise(ePKeyError, "BN_dup"); \ + } \ + \ + if (!_type##_set0_##_group(obj, bn1, bn2, bn3)) { \ + BN_clear_free(bn1); \ + BN_clear_free(bn2); \ + BN_clear_free(bn3); \ + ossl_raise(ePKeyError, #_type"_set0_"#_group); \ + } \ + return self; \ } -#define OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2) \ -/* \ - * call-seq: \ - * _keytype##.set_##_group(a1, a2) -> self \ - */ \ +#define OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2) \ +/* \ + * call-seq: \ + * _keytype##.set_##_group(a1, a2) -> self \ + */ \ static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2) \ -{ \ - _type *obj; \ - BIGNUM *bn1 = NULL, *orig_bn1 = NIL_P(v1) ? NULL : GetBNPtr(v1);\ - BIGNUM *bn2 = NULL, *orig_bn2 = NIL_P(v2) ? NULL : GetBNPtr(v2);\ - \ - Get##_type(self, obj); \ - if ((orig_bn1 && !(bn1 = BN_dup(orig_bn1))) || \ - (orig_bn2 && !(bn2 = BN_dup(orig_bn2)))) { \ - BN_clear_free(bn1); \ - BN_clear_free(bn2); \ - ossl_raise(eBNError, NULL); \ - } \ - \ - if (!_type##_set0_##_group(obj, bn1, bn2)) { \ - BN_clear_free(bn1); \ - BN_clear_free(bn2); \ - ossl_raise(ePKeyError, #_type"_set0_"#_group); \ - } \ - return self; \ +{ \ + _type *obj; \ + BIGNUM *bn1 = NULL, *orig_bn1 = NIL_P(v1) ? NULL : GetBNPtr(v1);\ + BIGNUM *bn2 = NULL, *orig_bn2 = NIL_P(v2) ? NULL : GetBNPtr(v2);\ + \ + Get##_type(self, obj); \ + if ((orig_bn1 && !(bn1 = BN_dup(orig_bn1))) || \ + (orig_bn2 && !(bn2 = BN_dup(orig_bn2)))) { \ + BN_clear_free(bn1); \ + BN_clear_free(bn2); \ + ossl_raise(ePKeyError, "BN_dup"); \ + } \ + \ + if (!_type##_set0_##_group(obj, bn1, bn2)) { \ + BN_clear_free(bn1); \ + BN_clear_free(bn2); \ + ossl_raise(ePKeyError, #_type"_set0_"#_group); \ + } \ + return self; \ } #else -#define OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) \ +#define OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) \ static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2, VALUE v3) \ -{ \ - rb_raise(ePKeyError, \ +{ \ + rb_raise(ePKeyError, \ #_keytype"#set_"#_group"= is incompatible with OpenSSL 3.0"); \ } -#define OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2) \ +#define OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2) \ static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2) \ -{ \ - rb_raise(ePKeyError, \ +{ \ + rb_raise(ePKeyError, \ #_keytype"#set_"#_group"= is incompatible with OpenSSL 3.0"); \ } #endif -#define OSSL_PKEY_BN_DEF3(_keytype, _type, _group, a1, a2, a3) \ - OSSL_PKEY_BN_DEF_GETTER3(_keytype, _type, _group, a1, a2, a3) \ - OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) +#define OSSL_PKEY_BN_DEF3(_keytype, _type, _group, a1, a2, a3) \ + OSSL_PKEY_BN_DEF_GETTER3(_keytype, _type, _group, a1, a2, a3) \ + OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) -#define OSSL_PKEY_BN_DEF2(_keytype, _type, _group, a1, a2) \ - OSSL_PKEY_BN_DEF_GETTER2(_keytype, _type, _group, a1, a2) \ - OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2) +#define OSSL_PKEY_BN_DEF2(_keytype, _type, _group, a1, a2) \ + OSSL_PKEY_BN_DEF_GETTER2(_keytype, _type, _group, a1, a2) \ + OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2) -#define DEF_OSSL_PKEY_BN(class, keytype, name) \ - rb_define_method((class), #name, ossl_##keytype##_get_##name, 0) +#define DEF_OSSL_PKEY_BN(class, keytype, name) \ + rb_define_method((class), #name, ossl_##keytype##_get_##name, 0) #endif /* OSSL_PKEY_H */ diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c index a231814a99..3f2975c5a3 100644 --- a/ext/openssl/ossl_pkey_dh.c +++ b/ext/openssl/ossl_pkey_dh.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -14,20 +14,21 @@ #define GetPKeyDH(obj, pkey) do { \ GetPKey((obj), (pkey)); \ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) { /* PARANOIA? */ \ - ossl_raise(rb_eRuntimeError, "THIS IS NOT A DH!") ; \ + ossl_raise(rb_eRuntimeError, "THIS IS NOT A DH!") ; \ } \ } while (0) #define GetDH(obj, dh) do { \ EVP_PKEY *_pkey; \ GetPKeyDH((obj), _pkey); \ (dh) = EVP_PKEY_get0_DH(_pkey); \ + if ((dh) == NULL) \ + ossl_raise(ePKeyError, "failed to get DH from EVP_PKEY"); \ } while (0) /* * Classes */ VALUE cDH; -VALUE eDHError; /* * Private @@ -43,6 +44,7 @@ VALUE eDHError; * If called without arguments, an empty instance without any parameter or key * components is created. Use #set_pqg to manually set the parameters afterwards * (and optionally #set_key to set private and public key components). + * This form is not compatible with OpenSSL 3.0 or later. * * If a String is given, tries to parse it as a DER- or PEM- encoded parameters. * See also OpenSSL::PKey.read which can parse keys of any kinds. @@ -58,14 +60,15 @@ VALUE eDHError; * * Examples: * # Creating an instance from scratch - * # Note that this is deprecated and will not work on OpenSSL 3.0 or later. + * # Note that this is deprecated and will result in ArgumentError when + * # using OpenSSL 3.0 or later. * dh = OpenSSL::PKey::DH.new * dh.set_pqg(bn_p, nil, bn_g) * * # Generating a parameters and a key pair * dh = OpenSSL::PKey::DH.new(2048) # An alias of OpenSSL::PKey::DH.generate(2048) * - * # Reading DH parameters + * # Reading DH parameters from a PEM-encoded string * dh_params = OpenSSL::PKey::DH.new(File.read('parameters.pem')) # loads parameters only * dh = OpenSSL::PKey.generate_key(dh_params) # generates a key pair */ @@ -84,10 +87,15 @@ ossl_dh_initialize(int argc, VALUE *argv, VALUE self) /* The DH.new(size, generator) form is handled by lib/openssl/pkey.rb */ if (rb_scan_args(argc, argv, "01", &arg) == 0) { +#ifdef OSSL_HAVE_IMMUTABLE_PKEY + rb_raise(rb_eArgError, "OpenSSL::PKey::DH.new cannot be called " \ + "without arguments; pkeys are immutable with OpenSSL 3.0"); +#else dh = DH_new(); if (!dh) - ossl_raise(eDHError, "DH_new"); + ossl_raise(ePKeyError, "DH_new"); goto legacy; +#endif } arg = ossl_to_der_if_possible(arg); @@ -105,12 +113,12 @@ ossl_dh_initialize(int argc, VALUE *argv, VALUE self) pkey = ossl_pkey_read_generic(in, Qnil); BIO_free(in); if (!pkey) - ossl_raise(eDHError, "could not parse pkey"); + ossl_raise(ePKeyError, "could not parse pkey"); type = EVP_PKEY_base_id(pkey); if (type != EVP_PKEY_DH) { EVP_PKEY_free(pkey); - rb_raise(eDHError, "incorrect pkey type: %s", OBJ_nid2sn(type)); + rb_raise(ePKeyError, "incorrect pkey type: %s", OBJ_nid2sn(type)); } RTYPEDDATA_DATA(self) = pkey; return self; @@ -121,13 +129,14 @@ ossl_dh_initialize(int argc, VALUE *argv, VALUE self) if (!pkey || EVP_PKEY_assign_DH(pkey, dh) != 1) { EVP_PKEY_free(pkey); DH_free(dh); - ossl_raise(eDHError, "EVP_PKEY_assign_DH"); + ossl_raise(ePKeyError, "EVP_PKEY_assign_DH"); } RTYPEDDATA_DATA(self) = pkey; return self; } #ifndef HAVE_EVP_PKEY_DUP +/* :nodoc: */ static VALUE ossl_dh_initialize_copy(VALUE self, VALUE other) { @@ -142,26 +151,26 @@ ossl_dh_initialize_copy(VALUE self, VALUE other) dh = DHparams_dup(dh_other); if (!dh) - ossl_raise(eDHError, "DHparams_dup"); + ossl_raise(ePKeyError, "DHparams_dup"); DH_get0_key(dh_other, &pub, &priv); if (pub) { - BIGNUM *pub2 = BN_dup(pub); - BIGNUM *priv2 = BN_dup(priv); + BIGNUM *pub2 = BN_dup(pub); + BIGNUM *priv2 = BN_dup(priv); if (!pub2 || (priv && !priv2)) { - BN_clear_free(pub2); - BN_clear_free(priv2); - ossl_raise(eDHError, "BN_dup"); - } - DH_set0_key(dh, pub2, priv2); + BN_clear_free(pub2); + BN_clear_free(priv2); + ossl_raise(ePKeyError, "BN_dup"); + } + DH_set0_key(dh, pub2, priv2); } pkey = EVP_PKEY_new(); if (!pkey || EVP_PKEY_assign_DH(pkey, dh) != 1) { EVP_PKEY_free(pkey); DH_free(dh); - ossl_raise(eDHError, "EVP_PKEY_assign_DH"); + ossl_raise(ePKeyError, "EVP_PKEY_assign_DH"); } RTYPEDDATA_DATA(self) = pkey; return self; @@ -240,11 +249,11 @@ ossl_dh_export(VALUE self) GetDH(self, dh); if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eDHError, NULL); + ossl_raise(ePKeyError, NULL); } if (!PEM_write_bio_DHparams(out, dh)) { - BIO_free(out); - ossl_raise(eDHError, NULL); + BIO_free(out); + ossl_raise(ePKeyError, NULL); } str = ossl_membio2str(out); @@ -274,11 +283,11 @@ ossl_dh_to_der(VALUE self) GetDH(self, dh); if((len = i2d_DHparams(dh, NULL)) <= 0) - ossl_raise(eDHError, NULL); + ossl_raise(ePKeyError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_DHparams(dh, &p) < 0) - ossl_raise(eDHError, NULL); + ossl_raise(ePKeyError, NULL); ossl_str_adjust(str, p); return str; @@ -286,35 +295,6 @@ ossl_dh_to_der(VALUE self) /* * call-seq: - * dh.params -> hash - * - * Stores all parameters of key to the hash - * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! - * Don't use :-)) (I's up to you) - */ -static VALUE -ossl_dh_get_params(VALUE self) -{ - OSSL_3_const DH *dh; - VALUE hash; - const BIGNUM *p, *q, *g, *pub_key, *priv_key; - - GetDH(self, dh); - DH_get0_pqg(dh, &p, &q, &g); - DH_get0_key(dh, &pub_key, &priv_key); - - hash = rb_hash_new(); - rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(p)); - rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(q)); - rb_hash_aset(hash, rb_str_new2("g"), ossl_bn_new(g)); - rb_hash_aset(hash, rb_str_new2("pub_key"), ossl_bn_new(pub_key)); - rb_hash_aset(hash, rb_str_new2("priv_key"), ossl_bn_new(priv_key)); - - return hash; -} - -/* - * call-seq: * dh.params_ok? -> true | false * * Validates the Diffie-Hellman parameters associated with this instance. @@ -334,7 +314,7 @@ ossl_dh_check_params(VALUE self) GetPKey(self, pkey); pctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL); if (!pctx) - ossl_raise(eDHError, "EVP_PKEY_CTX_new"); + ossl_raise(ePKeyError, "EVP_PKEY_CTX_new"); ret = EVP_PKEY_param_check(pctx); EVP_PKEY_CTX_free(pctx); #else @@ -377,19 +357,6 @@ OSSL_PKEY_BN_DEF2(dh, DH, key, pub_key, priv_key) void Init_ossl_dh(void) { -#if 0 - mPKey = rb_define_module_under(mOSSL, "PKey"); - cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); - ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); -#endif - - /* Document-class: OpenSSL::PKey::DHError - * - * Generic exception that is raised if an operation on a DH PKey - * fails unexpectedly or in case an instantiation of an instance of DH - * fails due to non-conformant input data. - */ - eDHError = rb_define_class_under(mPKey, "DHError", ePKeyError); /* Document-class: OpenSSL::PKey::DH * * An implementation of the Diffie-Hellman key exchange protocol based on @@ -443,8 +410,6 @@ Init_ossl_dh(void) DEF_OSSL_PKEY_BN(cDH, dh, priv_key); rb_define_method(cDH, "set_pqg", ossl_dh_set_pqg, 3); rb_define_method(cDH, "set_key", ossl_dh_set_key, 2); - - rb_define_method(cDH, "params", ossl_dh_get_params, 0); } #else /* defined NO_DH */ diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c index 058ce73888..041646a058 100644 --- a/ext/openssl/ossl_pkey_dsa.c +++ b/ext/openssl/ossl_pkey_dsa.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -14,13 +14,15 @@ #define GetPKeyDSA(obj, pkey) do { \ GetPKey((obj), (pkey)); \ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DSA) { /* PARANOIA? */ \ - ossl_raise(rb_eRuntimeError, "THIS IS NOT A DSA!"); \ + ossl_raise(rb_eRuntimeError, "THIS IS NOT A DSA!"); \ } \ } while (0) #define GetDSA(obj, dsa) do { \ EVP_PKEY *_pkey; \ GetPKeyDSA((obj), _pkey); \ (dsa) = EVP_PKEY_get0_DSA(_pkey); \ + if ((dsa) == NULL) \ + ossl_raise(ePKeyError, "failed to get DSA from EVP_PKEY"); \ } while (0) static inline int @@ -41,7 +43,6 @@ DSA_PRIVATE(VALUE obj, OSSL_3_const DSA *dsa) * Classes */ VALUE cDSA; -VALUE eDSAError; /* * Private @@ -56,6 +57,7 @@ VALUE eDSAError; * * If called without arguments, creates a new instance with no key components * set. They can be set individually by #set_pqg and #set_key. + * This form is not compatible with OpenSSL 3.0 or later. * * If called with a String, tries to parse as DER or PEM encoding of a \DSA key. * See also OpenSSL::PKey.read which can parse keys of any kinds. @@ -96,10 +98,15 @@ ossl_dsa_initialize(int argc, VALUE *argv, VALUE self) /* The DSA.new(size, generator) form is handled by lib/openssl/pkey.rb */ rb_scan_args(argc, argv, "02", &arg, &pass); if (argc == 0) { +#ifdef OSSL_HAVE_IMMUTABLE_PKEY + rb_raise(rb_eArgError, "OpenSSL::PKey::DSA.new cannot be called " \ + "without arguments; pkeys are immutable with OpenSSL 3.0"); +#else dsa = DSA_new(); if (!dsa) - ossl_raise(eDSAError, "DSA_new"); + ossl_raise(ePKeyError, "DSA_new"); goto legacy; +#endif } pass = ossl_pem_passwd_value(pass); @@ -117,12 +124,12 @@ ossl_dsa_initialize(int argc, VALUE *argv, VALUE self) pkey = ossl_pkey_read_generic(in, pass); BIO_free(in); if (!pkey) - ossl_raise(eDSAError, "Neither PUB key nor PRIV key"); + ossl_raise(ePKeyError, "Neither PUB key nor PRIV key"); type = EVP_PKEY_base_id(pkey); if (type != EVP_PKEY_DSA) { EVP_PKEY_free(pkey); - rb_raise(eDSAError, "incorrect pkey type: %s", OBJ_nid2sn(type)); + rb_raise(ePKeyError, "incorrect pkey type: %s", OBJ_nid2sn(type)); } RTYPEDDATA_DATA(self) = pkey; return self; @@ -133,13 +140,14 @@ ossl_dsa_initialize(int argc, VALUE *argv, VALUE self) if (!pkey || EVP_PKEY_assign_DSA(pkey, dsa) != 1) { EVP_PKEY_free(pkey); DSA_free(dsa); - ossl_raise(eDSAError, "EVP_PKEY_assign_DSA"); + ossl_raise(ePKeyError, "EVP_PKEY_assign_DSA"); } RTYPEDDATA_DATA(self) = pkey; return self; } #ifndef HAVE_EVP_PKEY_DUP +/* :nodoc: */ static VALUE ossl_dsa_initialize_copy(VALUE self, VALUE other) { @@ -155,13 +163,13 @@ ossl_dsa_initialize_copy(VALUE self, VALUE other) (d2i_of_void *)d2i_DSAPrivateKey, (char *)dsa); if (!dsa_new) - ossl_raise(eDSAError, "ASN1_dup"); + ossl_raise(ePKeyError, "ASN1_dup"); pkey = EVP_PKEY_new(); if (!pkey || EVP_PKEY_assign_DSA(pkey, dsa_new) != 1) { EVP_PKEY_free(pkey); DSA_free(dsa_new); - ossl_raise(eDSAError, "EVP_PKEY_assign_DSA"); + ossl_raise(ePKeyError, "EVP_PKEY_assign_DSA"); } RTYPEDDATA_DATA(self) = pkey; @@ -304,35 +312,6 @@ ossl_dsa_to_der(VALUE self) /* - * call-seq: - * dsa.params -> hash - * - * Stores all parameters of key to the hash - * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! - * Don't use :-)) (I's up to you) - */ -static VALUE -ossl_dsa_get_params(VALUE self) -{ - OSSL_3_const DSA *dsa; - VALUE hash; - const BIGNUM *p, *q, *g, *pub_key, *priv_key; - - GetDSA(self, dsa); - DSA_get0_pqg(dsa, &p, &q, &g); - DSA_get0_key(dsa, &pub_key, &priv_key); - - hash = rb_hash_new(); - rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(p)); - rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(q)); - rb_hash_aset(hash, rb_str_new2("g"), ossl_bn_new(g)); - rb_hash_aset(hash, rb_str_new2("pub_key"), ossl_bn_new(pub_key)); - rb_hash_aset(hash, rb_str_new2("priv_key"), ossl_bn_new(priv_key)); - - return hash; -} - -/* * Document-method: OpenSSL::PKey::DSA#set_pqg * call-seq: * dsa.set_pqg(p, q, g) -> self @@ -355,20 +334,6 @@ OSSL_PKEY_BN_DEF2(dsa, DSA, key, pub_key, priv_key) void Init_ossl_dsa(void) { -#if 0 - mPKey = rb_define_module_under(mOSSL, "PKey"); - cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); - ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); -#endif - - /* Document-class: OpenSSL::PKey::DSAError - * - * Generic exception that is raised if an operation on a DSA PKey - * fails unexpectedly or in case an instantiation of an instance of DSA - * fails due to non-conformant input data. - */ - eDSAError = rb_define_class_under(mPKey, "DSAError", ePKeyError); - /* Document-class: OpenSSL::PKey::DSA * * DSA, the Digital Signature Algorithm, is specified in NIST's @@ -396,8 +361,6 @@ Init_ossl_dsa(void) DEF_OSSL_PKEY_BN(cDSA, dsa, priv_key); rb_define_method(cDSA, "set_pqg", ossl_dsa_set_pqg, 3); rb_define_method(cDSA, "set_key", ossl_dsa_set_key, 2); - - rb_define_method(cDSA, "params", ossl_dsa_get_params, 0); } #else /* defined NO_DSA */ diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index 4b3a1fd0fe..35f031819d 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -15,25 +15,27 @@ static const rb_data_type_t ossl_ec_point_type; #define GetPKeyEC(obj, pkey) do { \ GetPKey((obj), (pkey)); \ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) { \ - ossl_raise(rb_eRuntimeError, "THIS IS NOT A EC PKEY!"); \ + ossl_raise(rb_eRuntimeError, "THIS IS NOT A EC PKEY!"); \ } \ } while (0) #define GetEC(obj, key) do { \ EVP_PKEY *_pkey; \ GetPKeyEC(obj, _pkey); \ (key) = EVP_PKEY_get0_EC_KEY(_pkey); \ + if ((key) == NULL) \ + ossl_raise(ePKeyError, "failed to get EC_KEY from EVP_PKEY"); \ } while (0) #define GetECGroup(obj, group) do { \ TypedData_Get_Struct(obj, EC_GROUP, &ossl_ec_group_type, group); \ if ((group) == NULL) \ - ossl_raise(eEC_GROUP, "EC_GROUP is not initialized"); \ + ossl_raise(eEC_GROUP, "EC_GROUP is not initialized"); \ } while (0) #define GetECPoint(obj, point) do { \ TypedData_Get_Struct(obj, EC_POINT, &ossl_ec_point_type, point); \ if ((point) == NULL) \ - ossl_raise(eEC_POINT, "EC_POINT is not initialized"); \ + ossl_raise(eEC_POINT, "EC_POINT is not initialized"); \ } while (0) #define GetECPointGroup(obj, group) do { \ VALUE _group = rb_attr_get(obj, id_i_group); \ @@ -41,17 +43,13 @@ static const rb_data_type_t ossl_ec_point_type; } while (0) VALUE cEC; -VALUE eECError; -VALUE cEC_GROUP; -VALUE eEC_GROUP; -VALUE cEC_POINT; -VALUE eEC_POINT; +static VALUE cEC_GROUP; +static VALUE eEC_GROUP; +static VALUE cEC_POINT; +static VALUE eEC_POINT; -static ID s_GFp, s_GF2m; - -static ID ID_uncompressed; -static ID ID_compressed; -static ID ID_hybrid; +static VALUE sym_GFp, sym_GF2m; +static VALUE sym_uncompressed, sym_compressed, sym_hybrid; static ID id_i_group; @@ -68,27 +66,27 @@ ec_key_new_from_group(VALUE arg) EC_KEY *ec; if (rb_obj_is_kind_of(arg, cEC_GROUP)) { - EC_GROUP *group; + EC_GROUP *group; - GetECGroup(arg, group); - if (!(ec = EC_KEY_new())) - ossl_raise(eECError, NULL); + GetECGroup(arg, group); + if (!(ec = EC_KEY_new())) + ossl_raise(ePKeyError, NULL); - if (!EC_KEY_set_group(ec, group)) { - EC_KEY_free(ec); - ossl_raise(eECError, NULL); - } + if (!EC_KEY_set_group(ec, group)) { + EC_KEY_free(ec); + ossl_raise(ePKeyError, NULL); + } } else { - int nid = OBJ_sn2nid(StringValueCStr(arg)); + int nid = OBJ_sn2nid(StringValueCStr(arg)); - if (nid == NID_undef) - ossl_raise(eECError, "invalid curve name"); + if (nid == NID_undef) + ossl_raise(ePKeyError, "invalid curve name"); - if (!(ec = EC_KEY_new_by_curve_name(nid))) - ossl_raise(eECError, NULL); + if (!(ec = EC_KEY_new_by_curve_name(nid))) + ossl_raise(ePKeyError, NULL); - EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE); - EC_KEY_set_conv_form(ec, POINT_CONVERSION_UNCOMPRESSED); + EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE); + EC_KEY_set_conv_form(ec, POINT_CONVERSION_UNCOMPRESSED); } return ec; @@ -115,12 +113,12 @@ ossl_ec_key_s_generate(VALUE klass, VALUE arg) if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, ec) != 1) { EVP_PKEY_free(pkey); EC_KEY_free(ec); - ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY"); + ossl_raise(ePKeyError, "EVP_PKEY_assign_EC_KEY"); } RTYPEDDATA_DATA(obj) = pkey; if (!EC_KEY_generate_key(ec)) - ossl_raise(eECError, "EC_KEY_generate_key"); + ossl_raise(ePKeyError, "EC_KEY_generate_key"); return obj; } @@ -150,9 +148,14 @@ static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "02", &arg, &pass); if (NIL_P(arg)) { +#ifdef OSSL_HAVE_IMMUTABLE_PKEY + rb_raise(rb_eArgError, "OpenSSL::PKey::EC.new cannot be called " \ + "without arguments; pkeys are immutable with OpenSSL 3.0"); +#else if (!(ec = EC_KEY_new())) - ossl_raise(eECError, "EC_KEY_new"); + ossl_raise(ePKeyError, "EC_KEY_new"); goto legacy; +#endif } else if (rb_obj_is_kind_of(arg, cEC_GROUP)) { ec = ec_key_new_from_group(arg); @@ -174,7 +177,7 @@ static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self) type = EVP_PKEY_base_id(pkey); if (type != EVP_PKEY_EC) { EVP_PKEY_free(pkey); - rb_raise(eDSAError, "incorrect pkey type: %s", OBJ_nid2sn(type)); + rb_raise(ePKeyError, "incorrect pkey type: %s", OBJ_nid2sn(type)); } RTYPEDDATA_DATA(self) = pkey; return self; @@ -184,13 +187,14 @@ static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self) if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, ec) != 1) { EVP_PKEY_free(pkey); EC_KEY_free(ec); - ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY"); + ossl_raise(ePKeyError, "EVP_PKEY_assign_EC_KEY"); } RTYPEDDATA_DATA(self) = pkey; return self; } #ifndef HAVE_EVP_PKEY_DUP +/* :nodoc: */ static VALUE ossl_ec_key_initialize_copy(VALUE self, VALUE other) { @@ -204,12 +208,12 @@ ossl_ec_key_initialize_copy(VALUE self, VALUE other) ec_new = EC_KEY_dup(ec); if (!ec_new) - ossl_raise(eECError, "EC_KEY_dup"); + ossl_raise(ePKeyError, "EC_KEY_dup"); pkey = EVP_PKEY_new(); if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, ec_new) != 1) { EC_KEY_free(ec_new); - ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY"); + ossl_raise(ePKeyError, "EVP_PKEY_assign_EC_KEY"); } RTYPEDDATA_DATA(self) = pkey; @@ -233,7 +237,7 @@ ossl_ec_key_get_group(VALUE self) GetEC(self, ec); group = EC_KEY_get0_group(ec); if (!group) - return Qnil; + return Qnil; return ec_group_new(group); } @@ -248,7 +252,7 @@ ossl_ec_key_get_group(VALUE self) static VALUE ossl_ec_key_set_group(VALUE self, VALUE group_v) { -#if OSSL_OPENSSL_PREREQ(3, 0, 0) +#ifdef OSSL_HAVE_IMMUTABLE_PKEY rb_raise(ePKeyError, "pkeys are immutable on OpenSSL 3.0"); #else EC_KEY *ec; @@ -258,7 +262,7 @@ ossl_ec_key_set_group(VALUE self, VALUE group_v) GetECGroup(group_v, group); if (EC_KEY_set_group(ec, group) != 1) - ossl_raise(eECError, "EC_KEY_set_group"); + ossl_raise(ePKeyError, "EC_KEY_set_group"); return group_v; #endif @@ -290,7 +294,7 @@ static VALUE ossl_ec_key_get_private_key(VALUE self) */ static VALUE ossl_ec_key_set_private_key(VALUE self, VALUE private_key) { -#if OSSL_OPENSSL_PREREQ(3, 0, 0) +#ifdef OSSL_HAVE_IMMUTABLE_PKEY rb_raise(ePKeyError, "pkeys are immutable on OpenSSL 3.0"); #else EC_KEY *ec; @@ -301,14 +305,14 @@ static VALUE ossl_ec_key_set_private_key(VALUE self, VALUE private_key) bn = GetBNPtr(private_key); switch (EC_KEY_set_private_key(ec, bn)) { - case 1: + case 1: break; - case 0: + case 0: if (bn == NULL) break; - /* fallthrough */ - default: - ossl_raise(eECError, "EC_KEY_set_private_key"); + /* fallthrough */ + default: + ossl_raise(ePKeyError, "EC_KEY_set_private_key"); } return private_key; @@ -341,7 +345,7 @@ static VALUE ossl_ec_key_get_public_key(VALUE self) */ static VALUE ossl_ec_key_set_public_key(VALUE self, VALUE public_key) { -#if OSSL_OPENSSL_PREREQ(3, 0, 0) +#ifdef OSSL_HAVE_IMMUTABLE_PKEY rb_raise(ePKeyError, "pkeys are immutable on OpenSSL 3.0"); #else EC_KEY *ec; @@ -352,14 +356,14 @@ static VALUE ossl_ec_key_set_public_key(VALUE self, VALUE public_key) GetECPoint(public_key, point); switch (EC_KEY_set_public_key(ec, point)) { - case 1: + case 1: break; - case 0: + case 0: if (point == NULL) break; - /* fallthrough */ - default: - ossl_raise(eECError, "EC_KEY_set_public_key"); + /* fallthrough */ + default: + ossl_raise(ePKeyError, "EC_KEY_set_public_key"); } return public_key; @@ -463,7 +467,7 @@ ossl_ec_key_export(int argc, VALUE *argv, VALUE self) GetEC(self, ec); if (EC_KEY_get0_public_key(ec) == NULL) - ossl_raise(eECError, "can't export - no public key set"); + ossl_raise(ePKeyError, "can't export - no public key set"); if (EC_KEY_get0_private_key(ec)) return ossl_pkey_export_traditional(argc, argv, self, 0); else @@ -491,7 +495,7 @@ ossl_ec_key_to_der(VALUE self) GetEC(self, ec); if (EC_KEY_get0_public_key(ec) == NULL) - ossl_raise(eECError, "can't export - no public key set"); + ossl_raise(ePKeyError, "can't export - no public key set"); if (EC_KEY_get0_private_key(ec)) return ossl_pkey_export_traditional(0, NULL, self, 1); else @@ -513,14 +517,14 @@ ossl_ec_key_to_der(VALUE self) */ static VALUE ossl_ec_key_generate_key(VALUE self) { -#if OSSL_OPENSSL_PREREQ(3, 0, 0) +#ifdef OSSL_HAVE_IMMUTABLE_PKEY rb_raise(ePKeyError, "pkeys are immutable on OpenSSL 3.0"); #else EC_KEY *ec; GetEC(self, ec); if (EC_KEY_generate_key(ec) != 1) - ossl_raise(eECError, "EC_KEY_generate_key"); + ossl_raise(ePKeyError, "EC_KEY_generate_key"); return self; #endif @@ -545,18 +549,18 @@ static VALUE ossl_ec_key_check_key(VALUE self) GetEC(self, ec); pctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL); if (!pctx) - ossl_raise(eECError, "EVP_PKEY_CTX_new"); + ossl_raise(ePKeyError, "EVP_PKEY_CTX_new"); if (EC_KEY_get0_private_key(ec) != NULL) { if (EVP_PKEY_check(pctx) != 1) { EVP_PKEY_CTX_free(pctx); - ossl_raise(eECError, "EVP_PKEY_check"); + ossl_raise(ePKeyError, "EVP_PKEY_check"); } } else { if (EVP_PKEY_public_check(pctx) != 1) { EVP_PKEY_CTX_free(pctx); - ossl_raise(eECError, "EVP_PKEY_public_check"); + ossl_raise(ePKeyError, "EVP_PKEY_public_check"); } } @@ -566,7 +570,7 @@ static VALUE ossl_ec_key_check_key(VALUE self) GetEC(self, ec); if (EC_KEY_check_key(ec) != 1) - ossl_raise(eECError, "EC_KEY_check_key"); + ossl_raise(ePKeyError, "EC_KEY_check_key"); #endif return Qtrue; @@ -584,7 +588,7 @@ ossl_ec_group_free(void *ptr) static const rb_data_type_t ossl_ec_group_type = { "OpenSSL/ec_group", { - 0, ossl_ec_group_free, + 0, ossl_ec_group_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -604,7 +608,7 @@ ec_group_new(const EC_GROUP *group) obj = ossl_ec_group_alloc(cEC_GROUP); group_new = EC_GROUP_dup(group); if (!group_new) - ossl_raise(eEC_GROUP, "EC_GROUP_dup"); + ossl_raise(eEC_GROUP, "EC_GROUP_dup"); RTYPEDDATA_DATA(obj) = group_new; return obj; @@ -632,7 +636,7 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) ossl_raise(rb_eRuntimeError, "EC_GROUP is already initialized"); switch (rb_scan_args(argc, argv, "13", &arg1, &arg2, &arg3, &arg4)) { - case 1: + case 1: if (rb_obj_is_kind_of(arg1, cEC_GROUP)) { const EC_GROUP *arg1_group; @@ -644,7 +648,7 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) group = PEM_read_bio_ECPKParameters(in, NULL, NULL, NULL); if (!group) { - OSSL_BIO_reset(in); + OSSL_BIO_reset(in); group = d2i_ECPKParameters_bio(in, NULL); } @@ -654,11 +658,14 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) const char *name = StringValueCStr(arg1); int nid = OBJ_sn2nid(name); - ossl_clear_error(); /* ignore errors in d2i_ECPKParameters_bio() */ + ossl_clear_error(); /* ignore errors in d2i_ECPKParameters_bio() */ if (nid == NID_undef) ossl_raise(eEC_GROUP, "unknown curve name (%"PRIsVALUE")", arg1); - +#if !defined(OPENSSL_IS_AWSLC) group = EC_GROUP_new_by_curve_name(nid); +#else /* EC_GROUPs are static and immutable by default in AWS-LC. */ + group = EC_GROUP_new_by_curve_name_mutable(nid); +#endif if (group == NULL) ossl_raise(eEC_GROUP, "unable to create curve (%"PRIsVALUE")", arg1); @@ -668,33 +675,34 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) } break; - case 4: + case 4: if (SYMBOL_P(arg1)) { - ID id = SYM2ID(arg1); EC_GROUP *(*new_curve)(const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *) = NULL; const BIGNUM *p = GetBNPtr(arg2); const BIGNUM *a = GetBNPtr(arg3); const BIGNUM *b = GetBNPtr(arg4); - if (id == s_GFp) { + if (arg1 == sym_GFp) { new_curve = EC_GROUP_new_curve_GFp; + } #if !defined(OPENSSL_NO_EC2M) - } else if (id == s_GF2m) { + else if (arg1 == sym_GF2m) { new_curve = EC_GROUP_new_curve_GF2m; + } #endif - } else { + else { ossl_raise(rb_eArgError, "unknown symbol, must be :GFp or :GF2m"); } if ((group = new_curve(p, a, b, ossl_bn_ctx)) == NULL) ossl_raise(eEC_GROUP, "EC_GROUP_new_by_GF*"); } else { - ossl_raise(rb_eArgError, "unknown argument, must be :GFp or :GF2m"); + ossl_raise(rb_eArgError, "unknown argument, must be :GFp or :GF2m"); } break; - default: - ossl_raise(rb_eArgError, "wrong number of arguments"); + default: + ossl_raise(rb_eArgError, "wrong number of arguments (given %d, expected 1 or 4)", argc); } ASSUME(group); @@ -703,6 +711,7 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) return self; } +/* :nodoc: */ static VALUE ossl_ec_group_initialize_copy(VALUE self, VALUE other) { @@ -710,12 +719,12 @@ ossl_ec_group_initialize_copy(VALUE self, VALUE other) TypedData_Get_Struct(self, EC_GROUP, &ossl_ec_group_type, group_new); if (group_new) - ossl_raise(eEC_GROUP, "EC::Group already initialized"); + ossl_raise(eEC_GROUP, "EC::Group already initialized"); GetECGroup(other, group); group_new = EC_GROUP_dup(group); if (!group_new) - ossl_raise(eEC_GROUP, "EC_GROUP_dup"); + ossl_raise(eEC_GROUP, "EC_GROUP_dup"); RTYPEDDATA_DATA(self) = group_new; return self; @@ -737,9 +746,9 @@ static VALUE ossl_ec_group_eql(VALUE a, VALUE b) GetECGroup(b, group2); switch (EC_GROUP_cmp(group1, group2, ossl_bn_ctx)) { - case 0: return Qtrue; - case 1: return Qfalse; - default: ossl_raise(eEC_GROUP, "EC_GROUP_cmp"); + case 0: return Qtrue; + case 1: return Qfalse; + default: ossl_raise(eEC_GROUP, "EC_GROUP_cmp"); } } @@ -759,7 +768,7 @@ static VALUE ossl_ec_group_get_generator(VALUE self) GetECGroup(self, group); generator = EC_GROUP_get0_generator(group); if (!generator) - return Qnil; + return Qnil; return ec_point_new(generator, group); } @@ -802,11 +811,10 @@ static VALUE ossl_ec_group_get_order(VALUE self) { VALUE bn_obj; BIGNUM *bn; - EC_GROUP *group = NULL; + EC_GROUP *group; GetECGroup(self, group); - - bn_obj = ossl_bn_new(NULL); + bn_obj = ossl_bn_new(BN_value_one()); bn = GetBNPtr(bn_obj); if (EC_GROUP_get_order(group, bn, ossl_bn_ctx) != 1) @@ -827,11 +835,10 @@ static VALUE ossl_ec_group_get_cofactor(VALUE self) { VALUE bn_obj; BIGNUM *bn; - EC_GROUP *group = NULL; + EC_GROUP *group; GetECGroup(self, group); - - bn_obj = ossl_bn_new(NULL); + bn_obj = ossl_bn_new(BN_value_one()); bn = GetBNPtr(bn_obj); if (EC_GROUP_get_cofactor(group, bn, ossl_bn_ctx) != 1) @@ -842,25 +849,23 @@ static VALUE ossl_ec_group_get_cofactor(VALUE self) /* * call-seq: - * group.curve_name => String + * group.curve_name -> string or nil * - * Returns the curve name (sn). + * Returns the curve name (short name) corresponding to this group, or +nil+ + * if \OpenSSL does not have an OID associated with the group. * * See the OpenSSL documentation for EC_GROUP_get_curve_name() */ static VALUE ossl_ec_group_get_curve_name(VALUE self) { - EC_GROUP *group = NULL; + EC_GROUP *group; int nid; GetECGroup(self, group); - if (group == NULL) - return Qnil; - nid = EC_GROUP_get_curve_name(group); - -/* BUG: an nid or asn1 object should be returned, maybe. */ - return rb_str_new2(OBJ_nid2sn(nid)); + if (nid == NID_undef) + return Qnil; + return rb_str_new_cstr(OBJ_nid2sn(nid)); } /* @@ -953,37 +958,36 @@ static VALUE ossl_ec_group_set_asn1_flag(VALUE self, VALUE flag_v) */ static VALUE ossl_ec_group_get_point_conversion_form(VALUE self) { - EC_GROUP *group = NULL; + EC_GROUP *group; point_conversion_form_t form; - VALUE ret; GetECGroup(self, group); form = EC_GROUP_get_point_conversion_form(group); switch (form) { - case POINT_CONVERSION_UNCOMPRESSED: ret = ID_uncompressed; break; - case POINT_CONVERSION_COMPRESSED: ret = ID_compressed; break; - case POINT_CONVERSION_HYBRID: ret = ID_hybrid; break; - default: ossl_raise(eEC_GROUP, "unsupported point conversion form: %d, this module should be updated", form); + case POINT_CONVERSION_UNCOMPRESSED: + return sym_uncompressed; + case POINT_CONVERSION_COMPRESSED: + return sym_compressed; + case POINT_CONVERSION_HYBRID: + return sym_hybrid; + default: + ossl_raise(eEC_GROUP, "unsupported point conversion form: %d, " \ + "this module should be updated", form); } - - return ID2SYM(ret); } static point_conversion_form_t parse_point_conversion_form_symbol(VALUE sym) { - ID id = SYM2ID(sym); - - if (id == ID_uncompressed) - return POINT_CONVERSION_UNCOMPRESSED; - else if (id == ID_compressed) - return POINT_CONVERSION_COMPRESSED; - else if (id == ID_hybrid) - return POINT_CONVERSION_HYBRID; - else - ossl_raise(rb_eArgError, "unsupported point conversion form %+"PRIsVALUE - " (expected :compressed, :uncompressed, or :hybrid)", sym); + if (sym == sym_uncompressed) + return POINT_CONVERSION_UNCOMPRESSED; + if (sym == sym_compressed) + return POINT_CONVERSION_COMPRESSED; + if (sym == sym_hybrid) + return POINT_CONVERSION_HYBRID; + ossl_raise(rb_eArgError, "unsupported point conversion form %+"PRIsVALUE + " (expected :compressed, :uncompressed, or :hybrid)", sym); } /* @@ -1088,20 +1092,20 @@ static VALUE ossl_ec_group_to_string(VALUE self, int format) ossl_raise(eEC_GROUP, "BIO_new(BIO_s_mem())"); switch(format) { - case EXPORT_PEM: + case EXPORT_PEM: i = PEM_write_bio_ECPKParameters(out, group); - break; - case EXPORT_DER: + break; + case EXPORT_DER: i = i2d_ECPKParameters_bio(out, group); - break; - default: + break; + default: BIO_free(out); - ossl_raise(rb_eRuntimeError, "unknown format (internal error)"); + ossl_raise(rb_eRuntimeError, "unknown format (internal error)"); } if (i != 1) { BIO_free(out); - ossl_raise(eECError, NULL); + ossl_raise(ePKeyError, NULL); } str = ossl_membio2str(out); @@ -1145,11 +1149,11 @@ static VALUE ossl_ec_group_to_text(VALUE self) GetECGroup(self, group); if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eEC_GROUP, "BIO_new(BIO_s_mem())"); + ossl_raise(eEC_GROUP, "BIO_new(BIO_s_mem())"); } if (!ECPKParameters_print(out, group, 0)) { - BIO_free(out); - ossl_raise(eEC_GROUP, NULL); + BIO_free(out); + ossl_raise(eEC_GROUP, NULL); } str = ossl_membio2str(out); @@ -1169,7 +1173,7 @@ ossl_ec_point_free(void *ptr) static const rb_data_type_t ossl_ec_point_type = { "OpenSSL/EC_POINT", { - 0, ossl_ec_point_free, + 0, ossl_ec_point_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -1189,7 +1193,7 @@ ec_point_new(const EC_POINT *point, const EC_GROUP *group) obj = ossl_ec_point_alloc(cEC_POINT); point_new = EC_POINT_dup(point, group); if (!point_new) - ossl_raise(eEC_POINT, "EC_POINT_dup"); + ossl_raise(eEC_POINT, "EC_POINT_dup"); RTYPEDDATA_DATA(obj) = point_new; rb_ivar_set(obj, id_i_group, ec_group_new(group)); @@ -1217,39 +1221,39 @@ static VALUE ossl_ec_point_initialize(int argc, VALUE *argv, VALUE self) TypedData_Get_Struct(self, EC_POINT, &ossl_ec_point_type, point); if (point) - rb_raise(eEC_POINT, "EC_POINT already initialized"); + rb_raise(eEC_POINT, "EC_POINT already initialized"); rb_scan_args(argc, argv, "11", &group_v, &arg2); if (rb_obj_is_kind_of(group_v, cEC_POINT)) { - if (argc != 1) - rb_raise(rb_eArgError, "invalid second argument"); - return ossl_ec_point_initialize_copy(self, group_v); + if (argc != 1) + rb_raise(rb_eArgError, "invalid second argument"); + return ossl_ec_point_initialize_copy(self, group_v); } GetECGroup(group_v, group); if (argc == 1) { - point = EC_POINT_new(group); - if (!point) - ossl_raise(eEC_POINT, "EC_POINT_new"); + point = EC_POINT_new(group); + if (!point) + ossl_raise(eEC_POINT, "EC_POINT_new"); } else { - if (rb_obj_is_kind_of(arg2, cBN)) { - point = EC_POINT_bn2point(group, GetBNPtr(arg2), NULL, ossl_bn_ctx); - if (!point) - ossl_raise(eEC_POINT, "EC_POINT_bn2point"); - } - else { - StringValue(arg2); - point = EC_POINT_new(group); - if (!point) - ossl_raise(eEC_POINT, "EC_POINT_new"); - if (!EC_POINT_oct2point(group, point, - (unsigned char *)RSTRING_PTR(arg2), - RSTRING_LEN(arg2), ossl_bn_ctx)) { - EC_POINT_free(point); - ossl_raise(eEC_POINT, "EC_POINT_oct2point"); - } - } + if (rb_obj_is_kind_of(arg2, cBN)) { + point = EC_POINT_bn2point(group, GetBNPtr(arg2), NULL, ossl_bn_ctx); + if (!point) + ossl_raise(eEC_POINT, "EC_POINT_bn2point"); + } + else { + StringValue(arg2); + point = EC_POINT_new(group); + if (!point) + ossl_raise(eEC_POINT, "EC_POINT_new"); + if (!EC_POINT_oct2point(group, point, + (unsigned char *)RSTRING_PTR(arg2), + RSTRING_LEN(arg2), ossl_bn_ctx)) { + EC_POINT_free(point); + ossl_raise(eEC_POINT, "EC_POINT_oct2point"); + } + } } RTYPEDDATA_DATA(self) = point; @@ -1258,6 +1262,7 @@ static VALUE ossl_ec_point_initialize(int argc, VALUE *argv, VALUE self) return self; } +/* :nodoc: */ static VALUE ossl_ec_point_initialize_copy(VALUE self, VALUE other) { @@ -1267,7 +1272,7 @@ ossl_ec_point_initialize_copy(VALUE self, VALUE other) TypedData_Get_Struct(self, EC_POINT, &ossl_ec_point_type, point_new); if (point_new) - ossl_raise(eEC_POINT, "EC::Point already initialized"); + ossl_raise(eEC_POINT, "EC::Point already initialized"); GetECPoint(other, point); group_v = rb_obj_dup(rb_attr_get(other, id_i_group)); @@ -1275,7 +1280,7 @@ ossl_ec_point_initialize_copy(VALUE self, VALUE other) point_new = EC_POINT_dup(point, group); if (!point_new) - ossl_raise(eEC_POINT, "EC_POINT_dup"); + ossl_raise(eEC_POINT, "EC_POINT_dup"); RTYPEDDATA_DATA(self) = point_new; rb_ivar_set(self, id_i_group, group_v); @@ -1302,9 +1307,9 @@ static VALUE ossl_ec_point_eql(VALUE a, VALUE b) GetECGroup(group_v1, group); switch (EC_POINT_cmp(group, point1, point2, ossl_bn_ctx)) { - case 0: return Qtrue; - case 1: return Qfalse; - default: ossl_raise(eEC_POINT, "EC_POINT_cmp"); + case 0: return Qtrue; + case 1: return Qfalse; + default: ossl_raise(eEC_POINT, "EC_POINT_cmp"); } UNREACHABLE; @@ -1323,9 +1328,9 @@ static VALUE ossl_ec_point_is_at_infinity(VALUE self) GetECPointGroup(self, group); switch (EC_POINT_is_at_infinity(group, point)) { - case 1: return Qtrue; - case 0: return Qfalse; - default: ossl_raise(eEC_POINT, "EC_POINT_is_at_infinity"); + case 1: return Qtrue; + case 0: return Qfalse; + default: ossl_raise(eEC_POINT, "EC_POINT_is_at_infinity"); } UNREACHABLE; @@ -1344,9 +1349,9 @@ static VALUE ossl_ec_point_is_on_curve(VALUE self) GetECPointGroup(self, group); switch (EC_POINT_is_on_curve(group, point, ossl_bn_ctx)) { - case 1: return Qtrue; - case 0: return Qfalse; - default: ossl_raise(eEC_POINT, "EC_POINT_is_on_curve"); + case 1: return Qtrue; + case 0: return Qfalse; + default: ossl_raise(eEC_POINT, "EC_POINT_is_on_curve"); } UNREACHABLE; @@ -1367,7 +1372,7 @@ static VALUE ossl_ec_point_make_affine(VALUE self) GetECPointGroup(self, group); rb_warn("OpenSSL::PKey::EC::Point#make_affine! is deprecated"); -#if !OSSL_OPENSSL_PREREQ(3, 0, 0) +#if !defined(OSSL_HAVE_IMMUTABLE_PKEY) && !defined(OPENSSL_IS_AWSLC) if (EC_POINT_make_affine(group, point, ossl_bn_ctx) != 1) ossl_raise(eEC_POINT, "EC_POINT_make_affine"); #endif @@ -1438,12 +1443,12 @@ ossl_ec_point_to_octet_string(VALUE self, VALUE conversion_form) len = EC_POINT_point2oct(group, point, form, NULL, 0, ossl_bn_ctx); if (!len) - ossl_raise(eEC_POINT, "EC_POINT_point2oct"); + ossl_raise(eEC_POINT, "EC_POINT_point2oct"); str = rb_str_new(NULL, (long)len); if (!EC_POINT_point2oct(group, point, form, - (unsigned char *)RSTRING_PTR(str), len, - ossl_bn_ctx)) - ossl_raise(eEC_POINT, "EC_POINT_point2oct"); + (unsigned char *)RSTRING_PTR(str), len, + ossl_bn_ctx)) + ossl_raise(eEC_POINT, "EC_POINT_point2oct"); return str; } @@ -1478,7 +1483,6 @@ static VALUE ossl_ec_point_add(VALUE self, VALUE other) /* * call-seq: * point.mul(bn1 [, bn2]) => point - * point.mul(bns, points [, bn2]) => point * * Performs elliptic curve point multiplication. * @@ -1486,11 +1490,9 @@ static VALUE ossl_ec_point_add(VALUE self, VALUE other) * generator of the group of _point_. _bn2_ may be omitted, and in that case, * the result is just <tt>bn1 * point</tt>. * - * The second form calculates <tt>bns[0] * point + bns[1] * points[0] + ... - * + bns[-1] * points[-1] + bn2 * G</tt>. _bn2_ may be omitted. _bns_ must be - * an array of OpenSSL::BN. _points_ must be an array of - * OpenSSL::PKey::EC::Point. Please note that <tt>points[0]</tt> is not - * multiplied by <tt>bns[0]</tt>, but <tt>bns[1]</tt>. + * Before version 4.0.0, and when compiled with OpenSSL 1.1.1 or older, this + * method allowed another form: + * point.mul(bns, points [, bn2]) => point */ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self) { @@ -1508,62 +1510,15 @@ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self) GetECPoint(result, point_result); rb_scan_args(argc, argv, "12", &arg1, &arg2, &arg3); - if (!RB_TYPE_P(arg1, T_ARRAY)) { - BIGNUM *bn = GetBNPtr(arg1); + if (RB_TYPE_P(arg1, T_ARRAY) || argc > 2) + rb_raise(rb_eNotImpError, "OpenSSL::PKey::EC::Point#mul with arrays " \ + "is no longer supported"); - if (!NIL_P(arg2)) - bn_g = GetBNPtr(arg2); - if (EC_POINT_mul(group, point_result, bn_g, point_self, bn, ossl_bn_ctx) != 1) - ossl_raise(eEC_POINT, NULL); - } else { -#if (defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3) || defined(LIBRESSL_VERSION_NUMBER) - rb_raise(rb_eNotImpError, "calling #mul with arrays is not" \ - "supported by this OpenSSL version"); -#else - /* - * bignums | arg1[0] | arg1[1] | arg1[2] | ... - * points | self | arg2[0] | arg2[1] | ... - */ - long i, num; - VALUE bns_tmp, tmp_p, tmp_b; - const EC_POINT **points; - const BIGNUM **bignums; - - Check_Type(arg1, T_ARRAY); - Check_Type(arg2, T_ARRAY); - if (RARRAY_LEN(arg1) != RARRAY_LEN(arg2) + 1) /* arg2 must be 1 larger */ - ossl_raise(rb_eArgError, "bns must be 1 longer than points; see the documentation"); - - rb_warning("OpenSSL::PKey::EC::Point#mul(ary, ary) is deprecated; " \ - "use #mul(bn) form instead"); - - num = RARRAY_LEN(arg1); - bns_tmp = rb_ary_tmp_new(num); - bignums = ALLOCV_N(const BIGNUM *, tmp_b, num); - for (i = 0; i < num; i++) { - VALUE item = RARRAY_AREF(arg1, i); - bignums[i] = GetBNPtr(item); - rb_ary_push(bns_tmp, item); - } - - points = ALLOCV_N(const EC_POINT *, tmp_p, num); - points[0] = point_self; /* self */ - for (i = 0; i < num - 1; i++) - GetECPoint(RARRAY_AREF(arg2, i), points[i + 1]); - - if (!NIL_P(arg3)) - bn_g = GetBNPtr(arg3); - - if (EC_POINTs_mul(group, point_result, bn_g, num, points, bignums, ossl_bn_ctx) != 1) { - ALLOCV_END(tmp_b); - ALLOCV_END(tmp_p); - ossl_raise(eEC_POINT, NULL); - } - - ALLOCV_END(tmp_b); - ALLOCV_END(tmp_p); -#endif - } + BIGNUM *bn = GetBNPtr(arg1); + if (!NIL_P(arg2)) + bn_g = GetBNPtr(arg2); + if (EC_POINT_mul(group, point_result, bn_g, point_self, bn, ossl_bn_ctx) != 1) + ossl_raise(eEC_POINT, NULL); return result; } @@ -1571,15 +1526,6 @@ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self) void Init_ossl_ec(void) { #undef rb_intern -#if 0 - mPKey = rb_define_module_under(mOSSL, "PKey"); - cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); -#endif - - eECError = rb_define_class_under(mPKey, "ECError", ePKeyError); - /* * Document-class: OpenSSL::PKey::EC * @@ -1601,17 +1547,15 @@ void Init_ossl_ec(void) eEC_GROUP = rb_define_class_under(cEC_GROUP, "Error", eOSSLError); eEC_POINT = rb_define_class_under(cEC_POINT, "Error", eOSSLError); - s_GFp = rb_intern("GFp"); - s_GF2m = rb_intern("GF2m"); + sym_GFp = ID2SYM(rb_intern_const("GFp")); + sym_GF2m = ID2SYM(rb_intern_const("GF2m")); - ID_uncompressed = rb_intern("uncompressed"); - ID_compressed = rb_intern("compressed"); - ID_hybrid = rb_intern("hybrid"); + sym_uncompressed = ID2SYM(rb_intern_const("uncompressed")); + sym_compressed = ID2SYM(rb_intern_const("compressed")); + sym_hybrid = ID2SYM(rb_intern_const("hybrid")); rb_define_const(cEC, "NAMED_CURVE", INT2NUM(OPENSSL_EC_NAMED_CURVE)); -#if defined(OPENSSL_EC_EXPLICIT_CURVE) rb_define_const(cEC, "EXPLICIT_CURVE", INT2NUM(OPENSSL_EC_EXPLICIT_CURVE)); -#endif rb_define_singleton_method(cEC, "builtin_curves", ossl_s_builtin_curves, 0); diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c index 389f76f309..039b2c6a34 100644 --- a/ext/openssl/ossl_pkey_rsa.c +++ b/ext/openssl/ossl_pkey_rsa.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -14,13 +14,15 @@ #define GetPKeyRSA(obj, pkey) do { \ GetPKey((obj), (pkey)); \ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) { /* PARANOIA? */ \ - ossl_raise(rb_eRuntimeError, "THIS IS NOT A RSA!") ; \ + ossl_raise(rb_eRuntimeError, "THIS IS NOT A RSA!") ; \ } \ } while (0) #define GetRSA(obj, rsa) do { \ EVP_PKEY *_pkey; \ GetPKeyRSA((obj), _pkey); \ (rsa) = EVP_PKEY_get0_RSA(_pkey); \ + if ((rsa) == NULL) \ + ossl_raise(ePKeyError, "failed to get RSA from EVP_PKEY"); \ } while (0) static inline int @@ -42,7 +44,6 @@ RSA_PRIVATE(VALUE obj, OSSL_3_const RSA *rsa) * Classes */ VALUE cRSA; -VALUE eRSAError; /* * Private @@ -59,6 +60,7 @@ VALUE eRSAError; * If called without arguments, creates a new instance with no key components * set. They can be set individually by #set_key, #set_factors, and * #set_crt_params. + * This form is not compatible with OpenSSL 3.0 or later. * * If called with a String, tries to parse as DER or PEM encoding of an \RSA key. * Note that if _password_ is not specified, but the key is encrypted with a @@ -89,10 +91,15 @@ ossl_rsa_initialize(int argc, VALUE *argv, VALUE self) /* The RSA.new(size, generator) form is handled by lib/openssl/pkey.rb */ rb_scan_args(argc, argv, "02", &arg, &pass); if (argc == 0) { - rsa = RSA_new(); +#ifdef OSSL_HAVE_IMMUTABLE_PKEY + rb_raise(rb_eArgError, "OpenSSL::PKey::RSA.new cannot be called " \ + "without arguments; pkeys are immutable with OpenSSL 3.0"); +#else + rsa = RSA_new(); if (!rsa) - ossl_raise(eRSAError, "RSA_new"); + ossl_raise(ePKeyError, "RSA_new"); goto legacy; +#endif } pass = ossl_pem_passwd_value(pass); @@ -113,12 +120,12 @@ ossl_rsa_initialize(int argc, VALUE *argv, VALUE self) pkey = ossl_pkey_read_generic(in, pass); BIO_free(in); if (!pkey) - ossl_raise(eRSAError, "Neither PUB key nor PRIV key"); + ossl_raise(ePKeyError, "Neither PUB key nor PRIV key"); type = EVP_PKEY_base_id(pkey); if (type != EVP_PKEY_RSA) { EVP_PKEY_free(pkey); - rb_raise(eRSAError, "incorrect pkey type: %s", OBJ_nid2sn(type)); + rb_raise(ePKeyError, "incorrect pkey type: %s", OBJ_nid2sn(type)); } RTYPEDDATA_DATA(self) = pkey; return self; @@ -129,13 +136,14 @@ ossl_rsa_initialize(int argc, VALUE *argv, VALUE self) if (!pkey || EVP_PKEY_assign_RSA(pkey, rsa) != 1) { EVP_PKEY_free(pkey); RSA_free(rsa); - ossl_raise(eRSAError, "EVP_PKEY_assign_RSA"); + ossl_raise(ePKeyError, "EVP_PKEY_assign_RSA"); } RTYPEDDATA_DATA(self) = pkey; return self; } #ifndef HAVE_EVP_PKEY_DUP +/* :nodoc: */ static VALUE ossl_rsa_initialize_copy(VALUE self, VALUE other) { @@ -151,12 +159,12 @@ ossl_rsa_initialize_copy(VALUE self, VALUE other) (d2i_of_void *)d2i_RSAPrivateKey, (char *)rsa); if (!rsa_new) - ossl_raise(eRSAError, "ASN1_dup"); + ossl_raise(ePKeyError, "ASN1_dup"); pkey = EVP_PKEY_new(); if (!pkey || EVP_PKEY_assign_RSA(pkey, rsa_new) != 1) { RSA_free(rsa_new); - ossl_raise(eRSAError, "EVP_PKEY_assign_RSA"); + ossl_raise(ePKeyError, "EVP_PKEY_assign_RSA"); } RTYPEDDATA_DATA(self) = pkey; @@ -311,7 +319,7 @@ ossl_rsa_to_der(VALUE self) * Signs _data_ using the Probabilistic Signature Scheme (RSA-PSS) and returns * the calculated signature. * - * RSAError will be raised if an error occurs. + * PKeyError will be raised if an error occurs. * * See #verify_pss for the verification operation. * @@ -340,7 +348,7 @@ ossl_rsa_to_der(VALUE self) static VALUE ossl_rsa_sign_pss(int argc, VALUE *argv, VALUE self) { - VALUE digest, data, options, kwargs[2], signature; + VALUE digest, data, options, kwargs[2], signature, mgf1md_holder, md_holder; static ID kwargs_ids[2]; EVP_PKEY *pkey; EVP_PKEY_CTX *pkey_ctx; @@ -350,46 +358,46 @@ ossl_rsa_sign_pss(int argc, VALUE *argv, VALUE self) int salt_len; if (!kwargs_ids[0]) { - kwargs_ids[0] = rb_intern_const("salt_length"); - kwargs_ids[1] = rb_intern_const("mgf1_hash"); + kwargs_ids[0] = rb_intern_const("salt_length"); + kwargs_ids[1] = rb_intern_const("mgf1_hash"); } rb_scan_args(argc, argv, "2:", &digest, &data, &options); rb_get_kwargs(options, kwargs_ids, 2, 0, kwargs); if (kwargs[0] == ID2SYM(rb_intern("max"))) - salt_len = -2; /* RSA_PSS_SALTLEN_MAX_SIGN */ + salt_len = -2; /* RSA_PSS_SALTLEN_MAX_SIGN */ else if (kwargs[0] == ID2SYM(rb_intern("digest"))) - salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */ + salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */ else - salt_len = NUM2INT(kwargs[0]); - mgf1md = ossl_evp_get_digestbyname(kwargs[1]); + salt_len = NUM2INT(kwargs[0]); + mgf1md = ossl_evp_md_fetch(kwargs[1], &mgf1md_holder); pkey = GetPrivPKeyPtr(self); buf_len = EVP_PKEY_size(pkey); - md = ossl_evp_get_digestbyname(digest); + md = ossl_evp_md_fetch(digest, &md_holder); StringValue(data); signature = rb_str_new(NULL, (long)buf_len); md_ctx = EVP_MD_CTX_new(); if (!md_ctx) - goto err; + goto err; if (EVP_DigestSignInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1) - goto err; + goto err; if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) - goto err; + goto err; if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len) != 1) - goto err; + goto err; if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1md) != 1) - goto err; + goto err; if (EVP_DigestSignUpdate(md_ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1) - goto err; + goto err; if (EVP_DigestSignFinal(md_ctx, (unsigned char *)RSTRING_PTR(signature), &buf_len) != 1) - goto err; + goto err; rb_str_set_len(signature, (long)buf_len); @@ -398,7 +406,7 @@ ossl_rsa_sign_pss(int argc, VALUE *argv, VALUE self) err: EVP_MD_CTX_free(md_ctx); - ossl_raise(eRSAError, NULL); + ossl_raise(ePKeyError, NULL); } /* @@ -408,7 +416,7 @@ ossl_rsa_sign_pss(int argc, VALUE *argv, VALUE self) * Verifies _data_ using the Probabilistic Signature Scheme (RSA-PSS). * * The return value is +true+ if the signature is valid, +false+ otherwise. - * RSAError will be raised if an error occurs. + * PKeyError will be raised if an error occurs. * * See #sign_pss for the signing operation and an example code. * @@ -427,7 +435,7 @@ ossl_rsa_sign_pss(int argc, VALUE *argv, VALUE self) static VALUE ossl_rsa_verify_pss(int argc, VALUE *argv, VALUE self) { - VALUE digest, signature, data, options, kwargs[2]; + VALUE digest, signature, data, options, kwargs[2], mgf1md_holder, md_holder; static ID kwargs_ids[2]; EVP_PKEY *pkey; EVP_PKEY_CTX *pkey_ctx; @@ -436,98 +444,61 @@ ossl_rsa_verify_pss(int argc, VALUE *argv, VALUE self) int result, salt_len; if (!kwargs_ids[0]) { - kwargs_ids[0] = rb_intern_const("salt_length"); - kwargs_ids[1] = rb_intern_const("mgf1_hash"); + kwargs_ids[0] = rb_intern_const("salt_length"); + kwargs_ids[1] = rb_intern_const("mgf1_hash"); } rb_scan_args(argc, argv, "3:", &digest, &signature, &data, &options); rb_get_kwargs(options, kwargs_ids, 2, 0, kwargs); if (kwargs[0] == ID2SYM(rb_intern("auto"))) - salt_len = -2; /* RSA_PSS_SALTLEN_AUTO */ + salt_len = -2; /* RSA_PSS_SALTLEN_AUTO */ else if (kwargs[0] == ID2SYM(rb_intern("digest"))) - salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */ + salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */ else - salt_len = NUM2INT(kwargs[0]); - mgf1md = ossl_evp_get_digestbyname(kwargs[1]); + salt_len = NUM2INT(kwargs[0]); + mgf1md = ossl_evp_md_fetch(kwargs[1], &mgf1md_holder); GetPKey(self, pkey); - md = ossl_evp_get_digestbyname(digest); + md = ossl_evp_md_fetch(digest, &md_holder); StringValue(signature); StringValue(data); md_ctx = EVP_MD_CTX_new(); if (!md_ctx) - goto err; + goto err; if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1) - goto err; + goto err; if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) - goto err; + goto err; if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len) != 1) - goto err; + goto err; if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1md) != 1) - goto err; + goto err; if (EVP_DigestVerifyUpdate(md_ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1) - goto err; + goto err; result = EVP_DigestVerifyFinal(md_ctx, - (unsigned char *)RSTRING_PTR(signature), - RSTRING_LEN(signature)); + (unsigned char *)RSTRING_PTR(signature), + RSTRING_LEN(signature)); + EVP_MD_CTX_free(md_ctx); switch (result) { case 0: - ossl_clear_error(); - EVP_MD_CTX_free(md_ctx); - return Qfalse; + ossl_clear_error(); + return Qfalse; case 1: - EVP_MD_CTX_free(md_ctx); - return Qtrue; + return Qtrue; default: - goto err; + ossl_raise(ePKeyError, "EVP_DigestVerifyFinal"); } err: EVP_MD_CTX_free(md_ctx); - ossl_raise(eRSAError, NULL); -} - -/* - * call-seq: - * rsa.params => hash - * - * THIS METHOD IS INSECURE, PRIVATE INFORMATION CAN LEAK OUT!!! - * - * Stores all parameters of key to the hash. The hash has keys 'n', 'e', 'd', - * 'p', 'q', 'dmp1', 'dmq1', 'iqmp'. - * - * Don't use :-)) (It's up to you) - */ -static VALUE -ossl_rsa_get_params(VALUE self) -{ - OSSL_3_const RSA *rsa; - VALUE hash; - const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp; - - GetRSA(self, rsa); - RSA_get0_key(rsa, &n, &e, &d); - RSA_get0_factors(rsa, &p, &q); - RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp); - - hash = rb_hash_new(); - rb_hash_aset(hash, rb_str_new2("n"), ossl_bn_new(n)); - rb_hash_aset(hash, rb_str_new2("e"), ossl_bn_new(e)); - rb_hash_aset(hash, rb_str_new2("d"), ossl_bn_new(d)); - rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(p)); - rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(q)); - rb_hash_aset(hash, rb_str_new2("dmp1"), ossl_bn_new(dmp1)); - rb_hash_aset(hash, rb_str_new2("dmq1"), ossl_bn_new(dmq1)); - rb_hash_aset(hash, rb_str_new2("iqmp"), ossl_bn_new(iqmp)); - - return hash; + ossl_raise(ePKeyError, NULL); } /* @@ -565,20 +536,6 @@ OSSL_PKEY_BN_DEF3(rsa, RSA, crt_params, dmp1, dmq1, iqmp) void Init_ossl_rsa(void) { -#if 0 - mPKey = rb_define_module_under(mOSSL, "PKey"); - cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); - ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); -#endif - - /* Document-class: OpenSSL::PKey::RSAError - * - * Generic exception that is raised if an operation on an RSA PKey - * fails unexpectedly or in case an instantiation of an instance of RSA - * fails due to non-conformant input data. - */ - eRSAError = rb_define_class_under(mPKey, "RSAError", ePKeyError); - /* Document-class: OpenSSL::PKey::RSA * * RSA is an asymmetric public key algorithm that has been formalized in @@ -617,8 +574,6 @@ Init_ossl_rsa(void) rb_define_method(cRSA, "set_factors", ossl_rsa_set_factors, 2); rb_define_method(cRSA, "set_crt_params", ossl_rsa_set_crt_params, 3); - rb_define_method(cRSA, "params", ossl_rsa_get_params, 0); - /* * TODO: Test it rb_define_method(cRSA, "blinding_on!", ossl_rsa_blinding_on, 0); diff --git a/ext/openssl/ossl_provider.c b/ext/openssl/ossl_provider.c index 981c6ccdc7..ea5abb8e48 100644 --- a/ext/openssl/ossl_provider.c +++ b/ext/openssl/ossl_provider.c @@ -1,12 +1,10 @@ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" #ifdef OSSL_USE_PROVIDER -# include <openssl/provider.h> - #define NewProvider(klass) \ TypedData_Wrap_Struct((klass), &ossl_provider_type, 0) #define SetProvider(obj, provider) do { \ @@ -187,11 +185,6 @@ ossl_provider_inspect(VALUE self) void Init_ossl_provider(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - cProvider = rb_define_class_under(mOSSL, "Provider", rb_cObject); eProviderError = rb_define_class_under(cProvider, "ProviderError", eOSSLError); diff --git a/ext/openssl/ossl_rand.c b/ext/openssl/ossl_rand.c index 659dc818b6..753f8b25f7 100644 --- a/ext/openssl/ossl_rand.c +++ b/ext/openssl/ossl_rand.c @@ -5,12 +5,12 @@ * All rights reserved. * * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" -VALUE mRandom; -VALUE eRandomError; +static VALUE mRandom; +static VALUE eRandomError; /* * call-seq: @@ -68,7 +68,7 @@ static VALUE ossl_rand_load_file(VALUE self, VALUE filename) { if(!RAND_load_file(StringValueCStr(filename), -1)) { - ossl_raise(eRandomError, NULL); + ossl_raise(eRandomError, NULL); } return Qtrue; } @@ -85,14 +85,14 @@ static VALUE ossl_rand_write_file(VALUE self, VALUE filename) { if (RAND_write_file(StringValueCStr(filename)) == -1) { - ossl_raise(eRandomError, NULL); + ossl_raise(eRandomError, NULL); } return Qtrue; } /* * call-seq: - * random_bytes(length) -> string + * random_bytes(length) -> string * * Generates a String with _length_ number of cryptographically strong * pseudo-random bytes. @@ -112,9 +112,9 @@ ossl_rand_bytes(VALUE self, VALUE len) str = rb_str_new(0, n); ret = RAND_bytes((unsigned char *)RSTRING_PTR(str), n); if (ret == 0) { - ossl_raise(eRandomError, "RAND_bytes"); + ossl_raise(eRandomError, "RAND_bytes"); } else if (ret == -1) { - ossl_raise(eRandomError, "RAND_bytes is not supported"); + ossl_raise(eRandomError, "RAND_bytes is not supported"); } return str; @@ -131,7 +131,7 @@ static VALUE ossl_rand_egd(VALUE self, VALUE filename) { if (RAND_egd(StringValueCStr(filename)) == -1) { - ossl_raise(eRandomError, NULL); + ossl_raise(eRandomError, NULL); } return Qtrue; } @@ -151,7 +151,7 @@ ossl_rand_egd_bytes(VALUE self, VALUE filename, VALUE len) int n = NUM2INT(len); if (RAND_egd_bytes(StringValueCStr(filename), n) == -1) { - ossl_raise(eRandomError, NULL); + ossl_raise(eRandomError, NULL); } return Qtrue; } @@ -175,11 +175,6 @@ ossl_rand_status(VALUE self) void Init_ossl_rand(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - mRandom = rb_define_module_under(mOSSL, "Random"); eRandomError = rb_define_class_under(mRandom, "RandomError", eOSSLError); @@ -189,9 +184,7 @@ Init_ossl_rand(void) rb_define_module_function(mRandom, "load_random_file", ossl_rand_load_file, 1); rb_define_module_function(mRandom, "write_random_file", ossl_rand_write_file, 1); rb_define_module_function(mRandom, "random_bytes", ossl_rand_bytes, 1); -#if OPENSSL_VERSION_NUMBER < 0x10101000 || defined(LIBRESSL_VERSION_NUMBER) rb_define_alias(rb_singleton_class(mRandom), "pseudo_bytes", "random_bytes"); -#endif #ifdef HAVE_RAND_EGD rb_define_module_function(mRandom, "egd", ossl_rand_egd, 1); rb_define_module_function(mRandom, "egd_bytes", ossl_rand_egd_bytes, 2); diff --git a/ext/openssl/ossl_rand.h b/ext/openssl/ossl_rand.h index 8f77a3b239..294986d017 100644 --- a/ext/openssl/ossl_rand.h +++ b/ext/openssl/ossl_rand.h @@ -5,14 +5,11 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(_OSSL_RAND_H_) #define _OSSL_RAND_H_ -extern VALUE mRandom; -extern VALUE eRandomError; - void Init_ossl_rand(void); #endif /* _OSSL_RAND_H_ */ diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 9f374b65ff..630d46e43f 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -7,7 +7,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -18,11 +18,6 @@ # define OSSL_USE_NEXTPROTONEG #endif -#if !defined(TLS1_3_VERSION) && \ - OSSL_LIBRESSL_PREREQ(3, 2, 0) && !OSSL_LIBRESSL_PREREQ(3, 4, 0) -# define TLS1_3_VERSION 0x0304 -#endif - #ifdef _WIN32 # define TO_SOCKET(s) _get_osfhandle(s) #else @@ -30,32 +25,30 @@ #endif #define GetSSLCTX(obj, ctx) do { \ - TypedData_Get_Struct((obj), SSL_CTX, &ossl_sslctx_type, (ctx)); \ + TypedData_Get_Struct((obj), SSL_CTX, &ossl_sslctx_type, (ctx)); \ } while (0) VALUE mSSL; static VALUE eSSLError; -VALUE cSSLContext; +static VALUE cSSLContext; VALUE cSSLSocket; static VALUE eSSLErrorWaitReadable; static VALUE eSSLErrorWaitWritable; -static ID id_call, ID_callback_state, id_tmp_dh_callback, - id_npn_protocols_encoded, id_each; +static ID id_call, ID_callback_state, id_npn_protocols_encoded, id_each; static VALUE sym_exception, sym_wait_readable, sym_wait_writable; static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode, - id_i_verify_depth, id_i_verify_callback, id_i_client_ca, - id_i_renegotiation_cb, id_i_cert, id_i_key, id_i_extra_chain_cert, - id_i_client_cert_cb, id_i_timeout, - id_i_session_id_context, id_i_session_get_cb, id_i_session_new_cb, - id_i_session_remove_cb, id_i_npn_select_cb, id_i_npn_protocols, - id_i_alpn_select_cb, id_i_alpn_protocols, id_i_servername_cb, - id_i_verify_hostname, id_i_keylog_cb; + id_i_verify_depth, id_i_verify_callback, id_i_client_ca, + id_i_renegotiation_cb, id_i_cert, id_i_key, id_i_extra_chain_cert, + id_i_client_cert_cb, id_i_timeout, + id_i_session_id_context, id_i_session_get_cb, id_i_session_new_cb, + id_i_session_remove_cb, id_i_npn_select_cb, id_i_npn_protocols, + id_i_alpn_select_cb, id_i_alpn_protocols, id_i_servername_cb, + id_i_verify_hostname, id_i_keylog_cb, id_i_tmp_dh_callback; static ID id_i_io, id_i_context, id_i_hostname; -static int ossl_ssl_ex_vcb_idx; static int ossl_ssl_ex_ptr_idx; static int ossl_sslctx_ex_ptr_idx; @@ -85,127 +78,24 @@ ossl_sslctx_s_alloc(VALUE klass) { SSL_CTX *ctx; long mode = 0 | - SSL_MODE_ENABLE_PARTIAL_WRITE | - SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | - SSL_MODE_RELEASE_BUFFERS; + SSL_MODE_ENABLE_PARTIAL_WRITE | + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | + SSL_MODE_RELEASE_BUFFERS; VALUE obj; obj = TypedData_Wrap_Struct(klass, &ossl_sslctx_type, 0); -#if OPENSSL_VERSION_NUMBER >= 0x10100000 || defined(LIBRESSL_VERSION_NUMBER) ctx = SSL_CTX_new(TLS_method()); -#else - ctx = SSL_CTX_new(SSLv23_method()); -#endif if (!ctx) { ossl_raise(eSSLError, "SSL_CTX_new"); } SSL_CTX_set_mode(ctx, mode); + SSL_CTX_set_dh_auto(ctx, 1); RTYPEDDATA_DATA(obj) = ctx; SSL_CTX_set_ex_data(ctx, ossl_sslctx_ex_ptr_idx, (void *)obj); -#if !defined(OPENSSL_NO_EC) && OPENSSL_VERSION_NUMBER < 0x10100000 && \ - !defined(LIBRESSL_VERSION_NUMBER) - /* We use SSL_CTX_set1_curves_list() to specify the curve used in ECDH. It - * allows to specify multiple curve names and OpenSSL will select - * automatically from them. In OpenSSL 1.0.2, the automatic selection has to - * be enabled explicitly. OpenSSL 1.1.0 and LibreSSL 2.6.1 removed the knob - * and it is always enabled. To uniform the behavior, we enable the - * automatic selection also in 1.0.2. Users can still disable ECDH by - * removing ECDH cipher suites by SSLContext#ciphers=. */ - if (!SSL_CTX_set_ecdh_auto(ctx, 1)) - ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto"); -#endif - return obj; } -static int -parse_proto_version(VALUE str) -{ - int i; - static const struct { - const char *name; - int version; - } map[] = { - { "SSL2", SSL2_VERSION }, - { "SSL3", SSL3_VERSION }, - { "TLS1", TLS1_VERSION }, - { "TLS1_1", TLS1_1_VERSION }, - { "TLS1_2", TLS1_2_VERSION }, -#ifdef TLS1_3_VERSION - { "TLS1_3", TLS1_3_VERSION }, -#endif - }; - - if (NIL_P(str)) - return 0; - if (RB_INTEGER_TYPE_P(str)) - return NUM2INT(str); - - if (SYMBOL_P(str)) - str = rb_sym2str(str); - StringValue(str); - for (i = 0; i < numberof(map); i++) - if (!strncmp(map[i].name, RSTRING_PTR(str), RSTRING_LEN(str))) - return map[i].version; - rb_raise(rb_eArgError, "unrecognized version %+"PRIsVALUE, str); -} - -/* - * call-seq: - * ctx.set_minmax_proto_version(min, max) -> nil - * - * Sets the minimum and maximum supported protocol versions. See #min_version= - * and #max_version=. - */ -static VALUE -ossl_sslctx_set_minmax_proto_version(VALUE self, VALUE min_v, VALUE max_v) -{ - SSL_CTX *ctx; - int min, max; - - GetSSLCTX(self, ctx); - min = parse_proto_version(min_v); - max = parse_proto_version(max_v); - -#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION - if (!SSL_CTX_set_min_proto_version(ctx, min)) - ossl_raise(eSSLError, "SSL_CTX_set_min_proto_version"); - if (!SSL_CTX_set_max_proto_version(ctx, max)) - ossl_raise(eSSLError, "SSL_CTX_set_max_proto_version"); -#else - { - unsigned long sum = 0, opts = 0; - int i; - static const struct { - int ver; - unsigned long opts; - } options_map[] = { - { SSL2_VERSION, SSL_OP_NO_SSLv2 }, - { SSL3_VERSION, SSL_OP_NO_SSLv3 }, - { TLS1_VERSION, SSL_OP_NO_TLSv1 }, - { TLS1_1_VERSION, SSL_OP_NO_TLSv1_1 }, - { TLS1_2_VERSION, SSL_OP_NO_TLSv1_2 }, -# if defined(TLS1_3_VERSION) - { TLS1_3_VERSION, SSL_OP_NO_TLSv1_3 }, -# endif - }; - - for (i = 0; i < numberof(options_map); i++) { - sum |= options_map[i].opts; - if ((min && min > options_map[i].ver) || - (max && max < options_map[i].ver)) { - opts |= options_map[i].opts; - } - } - SSL_CTX_clear_options(ctx, sum); - SSL_CTX_set_options(ctx, opts); - } -#endif - - return Qnil; -} - static VALUE ossl_call_client_cert_cb(VALUE obj) { @@ -214,7 +104,7 @@ ossl_call_client_cert_cb(VALUE obj) ctx_obj = rb_attr_get(obj, id_i_context); cb = rb_attr_get(ctx_obj, id_i_client_cert_cb); if (NIL_P(cb)) - return Qnil; + return Qnil; ary = rb_funcallv(cb, id_call, 1, &obj); Check_Type(ary, T_ARRAY); @@ -232,7 +122,7 @@ ossl_client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey) obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); ret = rb_protect(ossl_call_client_cert_cb, obj, NULL); if (NIL_P(ret)) - return 0; + return 0; *x509 = DupX509CertPtr(RARRAY_AREF(ret, 0)); *pkey = DupPKeyPtr(RARRAY_AREF(ret, 1)); @@ -243,8 +133,6 @@ ossl_client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey) #if !defined(OPENSSL_NO_DH) struct tmp_dh_callback_args { VALUE ssl_obj; - ID id; - int type; int is_export; int keylength; }; @@ -253,48 +141,36 @@ static VALUE ossl_call_tmp_dh_callback(VALUE arg) { struct tmp_dh_callback_args *args = (struct tmp_dh_callback_args *)arg; - VALUE cb, dh; - EVP_PKEY *pkey; + VALUE ctx_obj, cb, obj; + const DH *dh; - cb = rb_funcall(args->ssl_obj, args->id, 0); + ctx_obj = rb_attr_get(args->ssl_obj, id_i_context); + cb = rb_attr_get(ctx_obj, id_i_tmp_dh_callback); if (NIL_P(cb)) - return (VALUE)NULL; - dh = rb_funcall(cb, id_call, 3, args->ssl_obj, INT2NUM(args->is_export), - INT2NUM(args->keylength)); - pkey = GetPKeyPtr(dh); - if (EVP_PKEY_base_id(pkey) != args->type) - return (VALUE)NULL; + return (VALUE)NULL; - return (VALUE)pkey; + obj = rb_funcall(cb, id_call, 3, args->ssl_obj, INT2NUM(args->is_export), + INT2NUM(args->keylength)); + // TODO: We should riase if obj is not DH + dh = EVP_PKEY_get0_DH(GetPKeyPtr(obj)); + if (!dh) + ossl_clear_error(); + + return (VALUE)dh; } -#endif -#if !defined(OPENSSL_NO_DH) static DH * ossl_tmp_dh_callback(SSL *ssl, int is_export, int keylength) { - VALUE rb_ssl; - EVP_PKEY *pkey; - struct tmp_dh_callback_args args; int state; - - rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); - args.ssl_obj = rb_ssl; - args.id = id_tmp_dh_callback; - args.is_export = is_export; - args.keylength = keylength; - args.type = EVP_PKEY_DH; - - pkey = (EVP_PKEY *)rb_protect(ossl_call_tmp_dh_callback, - (VALUE)&args, &state); + VALUE rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); + struct tmp_dh_callback_args args = {rb_ssl, is_export, keylength}; + VALUE ret = rb_protect(ossl_call_tmp_dh_callback, (VALUE)&args, &state); if (state) { - rb_ivar_set(rb_ssl, ID_callback_state, INT2NUM(state)); - return NULL; + rb_ivar_set(rb_ssl, ID_callback_state, INT2NUM(state)); + return NULL; } - if (!pkey) - return NULL; - - return (DH *)EVP_PKEY_get0_DH(pkey); + return (DH *)ret; } #endif /* OPENSSL_NO_DH */ @@ -310,13 +186,13 @@ call_verify_certificate_identity(VALUE ctx_v) hostname = rb_attr_get(ssl_obj, id_i_hostname); if (!RTEST(hostname)) { - rb_warning("verify_hostname requires hostname to be set"); - return Qtrue; + rb_warning("verify_hostname requires hostname to be set"); + return Qtrue; } cert_obj = ossl_x509_new(X509_STORE_CTX_get_current_cert(ctx)); return rb_funcall(mSSL, rb_intern("verify_certificate_identity"), 2, - cert_obj, hostname); + cert_obj, hostname); } static int @@ -327,25 +203,21 @@ ossl_ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) int status; ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); - cb = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_vcb_idx); ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); sslctx_obj = rb_attr_get(ssl_obj, id_i_context); + cb = rb_attr_get(sslctx_obj, id_i_verify_callback); verify_hostname = rb_attr_get(sslctx_obj, id_i_verify_hostname); if (preverify_ok && RTEST(verify_hostname) && !SSL_is_server(ssl) && - !X509_STORE_CTX_get_error_depth(ctx)) { - ret = rb_protect(call_verify_certificate_identity, (VALUE)ctx, &status); - if (status) { - rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(status)); - return 0; - } + !X509_STORE_CTX_get_error_depth(ctx)) { + ret = rb_protect(call_verify_certificate_identity, (VALUE)ctx, &status); + if (status) { + rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(status)); + return 0; + } if (ret != Qtrue) { preverify_ok = 0; -#if defined(X509_V_ERR_HOSTNAME_MISMATCH) X509_STORE_CTX_set_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH); -#else - X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REJECTED); -#endif } } @@ -367,11 +239,7 @@ ossl_call_session_get_cb(VALUE ary) } static SSL_SESSION * -#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER >= 0x10100000 ossl_sslctx_session_get_cb(SSL *ssl, const unsigned char *buf, int len, int *copy) -#else -ossl_sslctx_session_get_cb(SSL *ssl, unsigned char *buf, int len, int *copy) -#endif { VALUE ary, ssl_obj, ret_obj; SSL_SESSION *sess; @@ -444,7 +312,7 @@ ossl_sslctx_session_new_cb(SSL *ssl, SSL_SESSION *sess) return 0; } -#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER) +#if !OSSL_IS_LIBRESSL /* * It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements * SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see @@ -517,7 +385,7 @@ ossl_sslctx_session_remove_cb(SSL_CTX *ctx, SSL_SESSION *sess) * when SSL_CTX_free() is called. */ if (rb_during_gc()) - return; + return; OSSL_Debug("SSL SESSION remove callback entered"); @@ -548,8 +416,9 @@ ossl_sslctx_add_extra_chain_cert_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arg)) GetSSLCTX(arg, ctx); x509 = DupX509CertPtr(i); - if(!SSL_CTX_add_extra_chain_cert(ctx, x509)){ - ossl_raise(eSSLError, NULL); + if (!SSL_CTX_add_extra_chain_cert(ctx, x509)) { + X509_free(x509); + ossl_raise(eSSLError, "SSL_CTX_add_extra_chain_cert"); } return i; @@ -558,52 +427,42 @@ ossl_sslctx_add_extra_chain_cert_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arg)) static VALUE ossl_sslctx_setup(VALUE self); static VALUE -ossl_call_servername_cb(VALUE ary) +ossl_call_servername_cb(VALUE arg) { - VALUE ssl_obj, sslctx_obj, cb, ret_obj; - - Check_Type(ary, T_ARRAY); - ssl_obj = rb_ary_entry(ary, 0); + SSL *ssl = (void *)arg; + const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (!servername) + return Qnil; - sslctx_obj = rb_attr_get(ssl_obj, id_i_context); - cb = rb_attr_get(sslctx_obj, id_i_servername_cb); - if (NIL_P(cb)) return Qnil; + VALUE ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); + VALUE sslctx_obj = rb_attr_get(ssl_obj, id_i_context); + VALUE cb = rb_attr_get(sslctx_obj, id_i_servername_cb); + VALUE ary = rb_assoc_new(ssl_obj, rb_str_new_cstr(servername)); - ret_obj = rb_funcallv(cb, id_call, 1, &ary); + VALUE ret_obj = rb_funcallv(cb, id_call, 1, &ary); if (rb_obj_is_kind_of(ret_obj, cSSLContext)) { - SSL *ssl; SSL_CTX *ctx2; - ossl_sslctx_setup(ret_obj); - GetSSL(ssl_obj, ssl); GetSSLCTX(ret_obj, ctx2); - SSL_set_SSL_CTX(ssl, ctx2); + if (!SSL_set_SSL_CTX(ssl, ctx2)) + ossl_raise(eSSLError, "SSL_set_SSL_CTX"); rb_ivar_set(ssl_obj, id_i_context, ret_obj); } else if (!NIL_P(ret_obj)) { - ossl_raise(rb_eArgError, "servername_cb must return an " - "OpenSSL::SSL::SSLContext object or nil"); + ossl_raise(rb_eArgError, "servername_cb must return an " + "OpenSSL::SSL::SSLContext object or nil"); } - return ret_obj; + return Qnil; } static int ssl_servername_cb(SSL *ssl, int *ad, void *arg) { - VALUE ary, ssl_obj; - int state = 0; - const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); - - if (!servername) - return SSL_TLSEXT_ERR_OK; - - ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); - ary = rb_ary_new2(2); - rb_ary_push(ary, ssl_obj); - rb_ary_push(ary, rb_str_new2(servername)); + int state; - rb_protect(ossl_call_servername_cb, ary, &state); + rb_protect(ossl_call_servername_cb, (VALUE)ssl, &state); if (state) { + VALUE ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(state)); return SSL_TLSEXT_ERR_ALERT_FATAL; } @@ -630,7 +489,7 @@ ssl_npn_encode_protocol_i(RB_BLOCK_CALL_FUNC_ARGLIST(cur, encoded)) int len = RSTRING_LENINT(cur); char len_byte; if (len < 1 || len > 255) - ossl_raise(eSSLError, "Advertised protocol must have length 1..255"); + ossl_raise(eSSLError, "Advertised protocol must have length 1..255"); /* Encode the length byte */ len_byte = len; rb_str_buf_cat(encoded, &len_byte, 1); @@ -664,16 +523,16 @@ npn_select_cb_common_i(VALUE tmp) /* assume OpenSSL verifies this format */ /* The format is len_1|proto_1|...|len_n|proto_n */ while (in < in_end) { - l = *in++; - rb_ary_push(protocols, rb_str_new((const char *)in, l)); - in += l; + l = *in++; + rb_ary_push(protocols, rb_str_new((const char *)in, l)); + in += l; } selected = rb_funcallv(args->cb, id_call, 1, &protocols); StringValue(selected); len = RSTRING_LEN(selected); if (len < 1 || len >= 256) { - ossl_raise(eSSLError, "Selected protocol name must have length 1..255"); + ossl_raise(eSSLError, "Selected protocol name must have length 1..255"); } return selected; @@ -681,8 +540,8 @@ npn_select_cb_common_i(VALUE tmp) static int ssl_npn_select_cb_common(SSL *ssl, VALUE cb, const unsigned char **out, - unsigned char *outlen, const unsigned char *in, - unsigned int inlen) + unsigned char *outlen, const unsigned char *in, + unsigned int inlen) { VALUE selected; int status; @@ -694,10 +553,10 @@ ssl_npn_select_cb_common(SSL *ssl, VALUE cb, const unsigned char **out, selected = rb_protect(npn_select_cb_common_i, (VALUE)&args, &status); if (status) { - VALUE ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); + VALUE ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); - rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(status)); - return SSL_TLSEXT_ERR_ALERT_FATAL; + rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(status)); + return SSL_TLSEXT_ERR_ALERT_FATAL; } *out = (unsigned char *)RSTRING_PTR(selected); @@ -709,7 +568,7 @@ ssl_npn_select_cb_common(SSL *ssl, VALUE cb, const unsigned char **out, #ifdef OSSL_USE_NEXTPROTONEG static int ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen, - void *arg) + void *arg) { VALUE protocols = rb_attr_get((VALUE)arg, id_npn_protocols_encoded); @@ -721,7 +580,7 @@ ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen, static int ssl_npn_select_cb(SSL *ssl, unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, void *arg) + const unsigned char *in, unsigned int inlen, void *arg) { VALUE sslctx_obj, cb; @@ -729,13 +588,13 @@ ssl_npn_select_cb(SSL *ssl, unsigned char **out, unsigned char *outlen, cb = rb_attr_get(sslctx_obj, id_i_npn_select_cb); return ssl_npn_select_cb_common(ssl, cb, (const unsigned char **)out, - outlen, in, inlen); + outlen, in, inlen); } #endif static int ssl_alpn_select_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, void *arg) + const unsigned char *in, unsigned int inlen, void *arg) { VALUE sslctx_obj, cb; @@ -752,12 +611,15 @@ ssl_info_cb(const SSL *ssl, int where, int val) int is_server = SSL_is_server((SSL *)ssl); if (is_server && where & SSL_CB_HANDSHAKE_START) { - ssl_renegotiation_cb(ssl); + ssl_renegotiation_cb(ssl); } } /* - * Gets various OpenSSL options. + * call-seq: + * ctx.options -> integer + * + * Gets various \OpenSSL options. */ static VALUE ossl_sslctx_get_options(VALUE self) @@ -772,7 +634,17 @@ ossl_sslctx_get_options(VALUE self) } /* - * Sets various OpenSSL options. + * call-seq: + * ctx.options = integer + * + * Sets various \OpenSSL options. The options are a bit field and can be + * combined with the bitwise OR operator (<tt>|</tt>). Available options are + * defined as constants in OpenSSL::SSL that begin with +OP_+. + * + * For backwards compatibility, passing +nil+ has the same effect as passing + * OpenSSL::SSL::OP_ALL. + * + * See also man page SSL_CTX_set_options(3). */ static VALUE ossl_sslctx_set_options(VALUE self, VALUE options) @@ -785,9 +657,9 @@ ossl_sslctx_set_options(VALUE self, VALUE options) SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx)); if (NIL_P(options)) { - SSL_CTX_set_options(ctx, SSL_OP_ALL); + SSL_CTX_set_options(ctx, SSL_OP_ALL); } else { - SSL_CTX_set_options(ctx, NUM2ULONG(options)); + SSL_CTX_set_options(ctx, NUM2ULONG(options)); } return self; @@ -817,23 +689,26 @@ ossl_sslctx_setup(VALUE self) GetSSLCTX(self, ctx); #if !defined(OPENSSL_NO_DH) - SSL_CTX_set_tmp_dh_callback(ctx, ossl_tmp_dh_callback); + if (!NIL_P(rb_attr_get(self, id_i_tmp_dh_callback))) { + SSL_CTX_set_tmp_dh_callback(ctx, ossl_tmp_dh_callback); + SSL_CTX_set_dh_auto(ctx, 0); + } #endif -#ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH +#if !defined(OPENSSL_IS_AWSLC) /* AWS-LC has no support for TLS 1.3 PHA. */ SSL_CTX_set_post_handshake_auth(ctx, 1); #endif val = rb_attr_get(self, id_i_cert_store); if (!NIL_P(val)) { - X509_STORE *store = GetX509StorePtr(val); /* NO NEED TO DUP */ - SSL_CTX_set_cert_store(ctx, store); - X509_STORE_up_ref(store); + X509_STORE *store = GetX509StorePtr(val); /* NO NEED TO DUP */ + SSL_CTX_set_cert_store(ctx, store); + X509_STORE_up_ref(store); } val = rb_attr_get(self, id_i_extra_chain_cert); if(!NIL_P(val)){ - rb_block_call(val, rb_intern("each"), 0, 0, ossl_sslctx_add_extra_chain_cert_i, self); + rb_block_call(val, rb_intern("each"), 0, 0, ossl_sslctx_add_extra_chain_cert_i, self); } /* private key may be bundled in certificate file. */ @@ -857,22 +732,22 @@ ossl_sslctx_setup(VALUE self) val = rb_attr_get(self, id_i_client_ca); if(!NIL_P(val)){ - if (RB_TYPE_P(val, T_ARRAY)) { - for(i = 0; i < RARRAY_LEN(val); i++){ - client_ca = GetX509CertPtr(RARRAY_AREF(val, i)); - if (!SSL_CTX_add_client_CA(ctx, client_ca)){ - /* Copies X509_NAME => FREE it. */ - ossl_raise(eSSLError, "SSL_CTX_add_client_CA"); - } - } + if (RB_TYPE_P(val, T_ARRAY)) { + for(i = 0; i < RARRAY_LEN(val); i++){ + client_ca = GetX509CertPtr(RARRAY_AREF(val, i)); + if (!SSL_CTX_add_client_CA(ctx, client_ca)){ + /* Copies X509_NAME => FREE it. */ + ossl_raise(eSSLError, "SSL_CTX_add_client_CA"); + } + } } - else{ - client_ca = GetX509CertPtr(val); /* NO DUP NEEDED. */ + else{ + client_ca = GetX509CertPtr(val); /* NO DUP NEEDED. */ if (!SSL_CTX_add_client_CA(ctx, client_ca)){ - /* Copies X509_NAME => FREE it. */ - ossl_raise(eSSLError, "SSL_CTX_add_client_CA"); + /* Copies X509_NAME => FREE it. */ + ossl_raise(eSSLError, "SSL_CTX_add_client_CA"); } - } + } } val = rb_attr_get(self, id_i_ca_file); @@ -895,7 +770,7 @@ ossl_sslctx_setup(VALUE self) verify_mode = NIL_P(val) ? SSL_VERIFY_NONE : NUM2INT(val); SSL_CTX_set_verify(ctx, verify_mode, ossl_ssl_verify_callback); if (RTEST(rb_attr_get(self, id_i_client_cert_cb))) - SSL_CTX_set_client_cert_cb(ctx, ossl_client_cert_cb); + SSL_CTX_set_client_cert_cb(ctx, ossl_client_cert_cb); val = rb_attr_get(self, id_i_timeout); if(!NIL_P(val)) SSL_CTX_set_timeout(ctx, NUM2LONG(val)); @@ -906,63 +781,63 @@ ossl_sslctx_setup(VALUE self) #ifdef OSSL_USE_NEXTPROTONEG val = rb_attr_get(self, id_i_npn_protocols); if (!NIL_P(val)) { - VALUE encoded = ssl_encode_npn_protocols(val); - rb_ivar_set(self, id_npn_protocols_encoded, encoded); - SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *)self); - OSSL_Debug("SSL NPN advertise callback added"); + VALUE encoded = ssl_encode_npn_protocols(val); + rb_ivar_set(self, id_npn_protocols_encoded, encoded); + SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *)self); + OSSL_Debug("SSL NPN advertise callback added"); } if (RTEST(rb_attr_get(self, id_i_npn_select_cb))) { - SSL_CTX_set_next_proto_select_cb(ctx, ssl_npn_select_cb, (void *) self); - OSSL_Debug("SSL NPN select callback added"); + SSL_CTX_set_next_proto_select_cb(ctx, ssl_npn_select_cb, (void *) self); + OSSL_Debug("SSL NPN select callback added"); } #endif val = rb_attr_get(self, id_i_alpn_protocols); if (!NIL_P(val)) { - VALUE rprotos = ssl_encode_npn_protocols(val); + VALUE rprotos = ssl_encode_npn_protocols(val); - /* returns 0 on success */ - if (SSL_CTX_set_alpn_protos(ctx, (unsigned char *)RSTRING_PTR(rprotos), - RSTRING_LENINT(rprotos))) - ossl_raise(eSSLError, "SSL_CTX_set_alpn_protos"); - OSSL_Debug("SSL ALPN values added"); + /* returns 0 on success */ + if (SSL_CTX_set_alpn_protos(ctx, (unsigned char *)RSTRING_PTR(rprotos), + RSTRING_LENINT(rprotos))) + ossl_raise(eSSLError, "SSL_CTX_set_alpn_protos"); + OSSL_Debug("SSL ALPN values added"); } if (RTEST(rb_attr_get(self, id_i_alpn_select_cb))) { - SSL_CTX_set_alpn_select_cb(ctx, ssl_alpn_select_cb, (void *) self); - OSSL_Debug("SSL ALPN select callback added"); + SSL_CTX_set_alpn_select_cb(ctx, ssl_alpn_select_cb, (void *) self); + OSSL_Debug("SSL ALPN select callback added"); } rb_obj_freeze(self); val = rb_attr_get(self, id_i_session_id_context); if (!NIL_P(val)){ - StringValue(val); - if (!SSL_CTX_set_session_id_context(ctx, (unsigned char *)RSTRING_PTR(val), - RSTRING_LENINT(val))){ - ossl_raise(eSSLError, "SSL_CTX_set_session_id_context"); - } + StringValue(val); + if (!SSL_CTX_set_session_id_context(ctx, (unsigned char *)RSTRING_PTR(val), + RSTRING_LENINT(val))){ + ossl_raise(eSSLError, "SSL_CTX_set_session_id_context"); + } } if (RTEST(rb_attr_get(self, id_i_session_get_cb))) { - SSL_CTX_sess_set_get_cb(ctx, ossl_sslctx_session_get_cb); - OSSL_Debug("SSL SESSION get callback added"); + SSL_CTX_sess_set_get_cb(ctx, ossl_sslctx_session_get_cb); + OSSL_Debug("SSL SESSION get callback added"); } if (RTEST(rb_attr_get(self, id_i_session_new_cb))) { - SSL_CTX_sess_set_new_cb(ctx, ossl_sslctx_session_new_cb); - OSSL_Debug("SSL SESSION new callback added"); + SSL_CTX_sess_set_new_cb(ctx, ossl_sslctx_session_new_cb); + OSSL_Debug("SSL SESSION new callback added"); } if (RTEST(rb_attr_get(self, id_i_session_remove_cb))) { - SSL_CTX_sess_set_remove_cb(ctx, ossl_sslctx_session_remove_cb); - OSSL_Debug("SSL SESSION remove callback added"); + SSL_CTX_sess_set_remove_cb(ctx, ossl_sslctx_session_remove_cb); + OSSL_Debug("SSL SESSION remove callback added"); } val = rb_attr_get(self, id_i_servername_cb); if (!NIL_P(val)) { SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb); - OSSL_Debug("SSL TLSEXT servername callback added"); + OSSL_Debug("SSL TLSEXT servername callback added"); } -#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER) +#if !OSSL_IS_LIBRESSL /* * It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements * SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see @@ -977,6 +852,93 @@ ossl_sslctx_setup(VALUE self) return Qtrue; } +static int +parse_proto_version(VALUE str) +{ + int i; + static const struct { + const char *name; + int version; + } map[] = { + { "SSL2", SSL2_VERSION }, + { "SSL3", SSL3_VERSION }, + { "TLS1", TLS1_VERSION }, + { "TLS1_1", TLS1_1_VERSION }, + { "TLS1_2", TLS1_2_VERSION }, + { "TLS1_3", TLS1_3_VERSION }, + }; + + if (NIL_P(str)) + return 0; + if (RB_INTEGER_TYPE_P(str)) + return NUM2INT(str); + + if (SYMBOL_P(str)) + str = rb_sym2str(str); + StringValue(str); + for (i = 0; i < numberof(map); i++) + if (!strncmp(map[i].name, RSTRING_PTR(str), RSTRING_LEN(str))) + return map[i].version; + rb_raise(rb_eArgError, "unrecognized version %+"PRIsVALUE, str); +} + +/* + * call-seq: + * ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION + * ctx.min_version = :TLS1_2 + * ctx.min_version = nil + * + * Sets the lower bound on the supported SSL/TLS protocol version. The + * version may be specified by an integer constant named + * OpenSSL::SSL::*_VERSION, a Symbol, or +nil+ which means "any version". + * + * === Example + * ctx = OpenSSL::SSL::SSLContext.new + * ctx.min_version = OpenSSL::SSL::TLS1_1_VERSION + * ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION + * + * sock = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx) + * sock.connect # Initiates a connection using either TLS 1.1 or TLS 1.2 + */ +static VALUE +ossl_sslctx_set_min_version(VALUE self, VALUE v) +{ + SSL_CTX *ctx; + int version; + + rb_check_frozen(self); + GetSSLCTX(self, ctx); + version = parse_proto_version(v); + + if (!SSL_CTX_set_min_proto_version(ctx, version)) + ossl_raise(eSSLError, "SSL_CTX_set_min_proto_version"); + return v; +} + +/* + * call-seq: + * ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION + * ctx.max_version = :TLS1_2 + * ctx.max_version = nil + * + * Sets the upper bound of the supported SSL/TLS protocol version. See + * #min_version= for the possible values. + */ +static VALUE +ossl_sslctx_set_max_version(VALUE self, VALUE v) +{ + SSL_CTX *ctx; + int version; + + rb_check_frozen(self); + GetSSLCTX(self, ctx); + version = parse_proto_version(v); + + if (!SSL_CTX_set_max_proto_version(ctx, version)) + ossl_raise(eSSLError, "SSL_CTX_set_max_proto_version"); + return v; +} + static VALUE ossl_ssl_cipher_to_ary(const SSL_CIPHER *cipher) { @@ -1026,11 +988,10 @@ static VALUE build_cipher_string(VALUE v) { VALUE str, elem; - int i; if (RB_TYPE_P(v, T_ARRAY)) { str = rb_str_new(0, 0); - for (i = 0; i < RARRAY_LEN(v); i++) { + for (long i = 0; i < RARRAY_LEN(v); i++) { elem = rb_ary_entry(v, i); if (RB_TYPE_P(elem, T_ARRAY)) elem = rb_ary_entry(elem, 0); elem = rb_String(elem); @@ -1051,9 +1012,14 @@ build_cipher_string(VALUE v) * ctx.ciphers = [name, ...] * ctx.ciphers = [[name, version, bits, alg_bits], ...] * - * Sets the list of available cipher suites for this context. Note in a server - * context some ciphers require the appropriate certificates. For example, an - * RSA cipher suite can only be chosen when an RSA certificate is available. + * Sets the list of available cipher suites for TLS 1.2 and below for this + * context. + * + * Note in a server context some ciphers require the appropriate certificates. + * For example, an RSA cipher suite can only be chosen when an RSA certificate + * is available. + * + * This method does not affect TLS 1.3 connections. See also #ciphersuites=. */ static VALUE ossl_sslctx_set_ciphers(VALUE self, VALUE v) @@ -1062,6 +1028,7 @@ ossl_sslctx_set_ciphers(VALUE self, VALUE v) VALUE str; rb_check_frozen(self); + // Assigning nil is a no-op for compatibility if (NIL_P(v)) return v; @@ -1074,14 +1041,12 @@ ossl_sslctx_set_ciphers(VALUE self, VALUE v) return v; } -#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES /* * call-seq: * ctx.ciphersuites = "cipher1:cipher2:..." * ctx.ciphersuites = [name, ...] - * ctx.ciphersuites = [[name, version, bits, alg_bits], ...] * - * Sets the list of available TLSv1.3 cipher suites for this context. + * Sets the list of available TLS 1.3 cipher suites for this context. */ static VALUE ossl_sslctx_set_ciphersuites(VALUE self, VALUE v) @@ -1090,6 +1055,7 @@ ossl_sslctx_set_ciphersuites(VALUE self, VALUE v) VALUE str; rb_check_frozen(self); + // Assigning nil is a no-op for compatibility if (NIL_P(v)) return v; @@ -1101,6 +1067,62 @@ ossl_sslctx_set_ciphersuites(VALUE self, VALUE v) return v; } + +#ifdef HAVE_SSL_CTX_SET1_SIGALGS_LIST +/* + * call-seq: + * ctx.sigalgs = "sigalg1:sigalg2:..." + * + * Sets the list of "supported signature algorithms" for this context. + * + * For a TLS client, the list is used in the "signature_algorithms" extension + * in the ClientHello message. For a server, the list is used by OpenSSL to + * determine the set of shared signature algorithms. OpenSSL will pick the most + * appropriate one from it. + * + * See also #client_sigalgs= for the client authentication equivalent. + */ +static VALUE +ossl_sslctx_set_sigalgs(VALUE self, VALUE v) +{ + SSL_CTX *ctx; + + rb_check_frozen(self); + GetSSLCTX(self, ctx); + + if (!SSL_CTX_set1_sigalgs_list(ctx, StringValueCStr(v))) + ossl_raise(eSSLError, "SSL_CTX_set1_sigalgs_list"); + + return v; +} +#endif + +#ifdef HAVE_SSL_CTX_SET1_CLIENT_SIGALGS_LIST +/* + * call-seq: + * ctx.client_sigalgs = "sigalg1:sigalg2:..." + * + * Sets the list of "supported signature algorithms" for client authentication + * for this context. + * + * For a TLS server, the list is sent to the client as part of the + * CertificateRequest message. + * + * See also #sigalgs= for the server authentication equivalent. + */ +static VALUE +ossl_sslctx_set_client_sigalgs(VALUE self, VALUE v) +{ + SSL_CTX *ctx; + + rb_check_frozen(self); + GetSSLCTX(self, ctx); + + if (!SSL_CTX_set1_client_sigalgs_list(ctx, StringValueCStr(v))) + ossl_raise(eSSLError, "SSL_CTX_set1_client_sigalgs_list"); + + return v; +} #endif #ifndef OPENSSL_NO_DH @@ -1115,7 +1137,7 @@ ossl_sslctx_set_ciphersuites(VALUE self, VALUE v) * contained in the key object, if any, are ignored. The server will always * generate a new key pair for each handshake. * - * Added in version 3.0. See also the man page SSL_set0_tmp_dh_pkey(3). + * Added in version 3.0. See also the man page SSL_CTX_set0_tmp_dh_pkey(3). * * Example: * ctx = OpenSSL::SSL::SSLContext.new @@ -1136,7 +1158,7 @@ ossl_sslctx_set_tmp_dh(VALUE self, VALUE arg) if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) rb_raise(eSSLError, "invalid pkey type %s (expected DH)", OBJ_nid2sn(EVP_PKEY_base_id(pkey))); -#ifdef HAVE_SSL_SET0_TMP_DH_PKEY +#ifdef HAVE_SSL_CTX_SET0_TMP_DH_PKEY if (!SSL_CTX_set0_tmp_dh_pkey(ctx, pkey)) ossl_raise(eSSLError, "SSL_CTX_set0_tmp_dh_pkey"); EVP_PKEY_up_ref(pkey); @@ -1145,29 +1167,36 @@ ossl_sslctx_set_tmp_dh(VALUE self, VALUE arg) ossl_raise(eSSLError, "SSL_CTX_set_tmp_dh"); #endif + // Turn off the "auto" DH parameters set by ossl_sslctx_s_alloc() + SSL_CTX_set_dh_auto(ctx, 0); + return arg; } #endif -#if !defined(OPENSSL_NO_EC) /* * call-seq: - * ctx.ecdh_curves = curve_list -> curve_list + * ctx.groups = groups_list + * ctx.ecdh_curves = groups_list * - * Sets the list of "supported elliptic curves" for this context. + * Sets the list of supported groups for key agreement for this context. * - * For a TLS client, the list is directly used in the Supported Elliptic Curves - * Extension. For a server, the list is used by OpenSSL to determine the set of - * shared curves. OpenSSL will pick the most appropriate one from it. + * For a TLS client, the list is directly used in the "supported_groups" + * extension. For a server, the list is used by OpenSSL to determine the set of + * shared supported groups. OpenSSL will pick the most appropriate one from it. + * + * #ecdh_curves= is a deprecated alias for #groups=. + * + * See also the man page SSL_CTX_set1_groups_list(3). * * === Example * ctx1 = OpenSSL::SSL::SSLContext.new - * ctx1.ecdh_curves = "X25519:P-256:P-224" + * ctx1.groups = "X25519:P-256:P-224" * svr = OpenSSL::SSL::SSLServer.new(tcp_svr, ctx1) * Thread.new { svr.accept } * * ctx2 = OpenSSL::SSL::SSLContext.new - * ctx2.ecdh_curves = "P-256" + * ctx2.groups = "P-256" * cli = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx2) * cli.connect * @@ -1175,7 +1204,7 @@ ossl_sslctx_set_tmp_dh(VALUE self, VALUE arg) * # => "prime256v1" (is an alias for NIST P-256) */ static VALUE -ossl_sslctx_set_ecdh_curves(VALUE self, VALUE arg) +ossl_sslctx_set_groups(VALUE self, VALUE arg) { SSL_CTX *ctx; @@ -1183,13 +1212,10 @@ ossl_sslctx_set_ecdh_curves(VALUE self, VALUE arg) GetSSLCTX(self, ctx); StringValueCStr(arg); - if (!SSL_CTX_set1_curves_list(ctx, RSTRING_PTR(arg))) - ossl_raise(eSSLError, NULL); + if (!SSL_CTX_set1_groups_list(ctx, RSTRING_PTR(arg))) + ossl_raise(eSSLError, "SSL_CTX_set1_groups_list"); return arg; } -#else -#define ossl_sslctx_set_ecdh_curves rb_f_notimplement -#endif /* * call-seq: @@ -1206,12 +1232,7 @@ ossl_sslctx_get_security_level(VALUE self) GetSSLCTX(self, ctx); -#if defined(HAVE_SSL_CTX_GET_SECURITY_LEVEL) return INT2NUM(SSL_CTX_get_security_level(ctx)); -#else - (void)ctx; - return INT2FIX(0); -#endif } /* @@ -1241,14 +1262,7 @@ ossl_sslctx_set_security_level(VALUE self, VALUE value) rb_check_frozen(self); GetSSLCTX(self, ctx); -#if defined(HAVE_SSL_CTX_GET_SECURITY_LEVEL) SSL_CTX_set_security_level(ctx, NUM2INT(value)); -#else - (void)ctx; - if (NUM2INT(value) != 0) - ossl_raise(rb_eNotImpError, "setting security level to other than 0 is " - "not supported in this version of OpenSSL"); -#endif return value; } @@ -1330,20 +1344,20 @@ ossl_sslctx_add_certificate(int argc, VALUE *argv, VALUE self) pub_pkey = X509_get_pubkey(x509); EVP_PKEY_free(pub_pkey); if (!pub_pkey) - rb_raise(rb_eArgError, "certificate does not contain public key"); + rb_raise(rb_eArgError, "certificate does not contain public key"); if (EVP_PKEY_eq(pub_pkey, pkey) != 1) - rb_raise(rb_eArgError, "public key mismatch"); + rb_raise(rb_eArgError, "public key mismatch"); if (argc >= 3) - extra_chain = ossl_x509_ary2sk(extra_chain_ary); + extra_chain = ossl_x509_ary2sk(extra_chain_ary); if (!SSL_CTX_use_certificate(ctx, x509)) { - sk_X509_pop_free(extra_chain, X509_free); - ossl_raise(eSSLError, "SSL_CTX_use_certificate"); + sk_X509_pop_free(extra_chain, X509_free); + ossl_raise(eSSLError, "SSL_CTX_use_certificate"); } if (!SSL_CTX_use_PrivateKey(ctx, pkey)) { - sk_X509_pop_free(extra_chain, X509_free); - ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey"); + sk_X509_pop_free(extra_chain, X509_free); + ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey"); } if (extra_chain && !SSL_CTX_set0_chain(ctx, extra_chain)) { sk_X509_pop_free(extra_chain, X509_free); @@ -1553,11 +1567,6 @@ ossl_ssl_mark(void *ptr) { SSL *ssl = ptr; rb_gc_mark((VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)); - - // Note: this reference is stored as @verify_callback so we don't need to mark it. - // However we do need to ensure GC compaction won't move it, hence why - // we call rb_gc_mark here. - rb_gc_mark((VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_vcb_idx)); } static void @@ -1622,37 +1631,33 @@ peeraddr_ip_str(VALUE self) static VALUE ossl_ssl_initialize(int argc, VALUE *argv, VALUE self) { - VALUE io, v_ctx, verify_cb; + VALUE io, v_ctx; SSL *ssl; SSL_CTX *ctx; TypedData_Get_Struct(self, SSL, &ossl_ssl_type, ssl); if (ssl) - ossl_raise(eSSLError, "SSL already initialized"); + ossl_raise(eSSLError, "SSL already initialized"); if (rb_scan_args(argc, argv, "11", &io, &v_ctx) == 1) - v_ctx = rb_funcall(cSSLContext, rb_intern("new"), 0); + v_ctx = rb_funcall(cSSLContext, rb_intern("new"), 0); GetSSLCTX(v_ctx, ctx); rb_ivar_set(self, id_i_context, v_ctx); ossl_sslctx_setup(v_ctx); if (rb_respond_to(io, rb_intern("nonblock="))) - rb_funcall(io, rb_intern("nonblock="), 1, Qtrue); + rb_funcall(io, rb_intern("nonblock="), 1, Qtrue); Check_Type(io, T_FILE); rb_ivar_set(self, id_i_io, io); ssl = SSL_new(ctx); if (!ssl) - ossl_raise(eSSLError, NULL); + ossl_raise(eSSLError, NULL); RTYPEDDATA_DATA(self) = ssl; SSL_set_ex_data(ssl, ossl_ssl_ex_ptr_idx, (void *)self); SSL_set_info_callback(ssl, ssl_info_cb); - verify_cb = rb_attr_get(v_ctx, id_i_verify_callback); - // We don't need to trigger a write barrier because it's already - // an instance variable of this object. - SSL_set_ex_data(ssl, ossl_ssl_ex_vcb_idx, (void *)verify_cb); rb_call_super(0, NULL); @@ -1679,7 +1684,7 @@ ossl_ssl_setup(VALUE self) GetSSL(self, ssl); if (ssl_started(ssl)) - return Qtrue; + return Qtrue; io = rb_attr_get(self, id_i_io); GetOpenFile(io, fptr); @@ -1701,14 +1706,14 @@ static void write_would_block(int nonblock) { if (nonblock) - ossl_raise(eSSLErrorWaitWritable, "write would block"); + ossl_raise(eSSLErrorWaitWritable, "write would block"); } static void read_would_block(int nonblock) { if (nonblock) - ossl_raise(eSSLErrorWaitReadable, "read would block"); + ossl_raise(eSSLErrorWaitReadable, "read would block"); } static int @@ -1716,7 +1721,7 @@ no_exception_p(VALUE opts) { if (RB_TYPE_P(opts, T_HASH) && rb_hash_lookup2(opts, sym_exception, Qundef) == Qfalse) - return 1; + return 1; return 0; } @@ -1936,13 +1941,13 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) { SSL *ssl; int ilen; - VALUE len, str; + VALUE len, str, cb_state; VALUE opts = Qnil; if (nonblock) { - rb_scan_args(argc, argv, "11:", &len, &str, &opts); + rb_scan_args(argc, argv, "11:", &len, &str, &opts); } else { - rb_scan_args(argc, argv, "11", &len, &str); + rb_scan_args(argc, argv, "11", &len, &str); } GetSSL(self, ssl); if (!ssl_started(ssl)) @@ -1950,51 +1955,57 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) ilen = NUM2INT(len); if (NIL_P(str)) - str = rb_str_new(0, ilen); + str = rb_str_new(0, ilen); else { - StringValue(str); - if (RSTRING_LEN(str) >= ilen) - rb_str_modify(str); - else - rb_str_modify_expand(str, ilen - RSTRING_LEN(str)); + StringValue(str); + if (RSTRING_LEN(str) >= ilen) + rb_str_modify(str); + else + rb_str_modify_expand(str, ilen - RSTRING_LEN(str)); + } + + if (ilen == 0) { + rb_str_set_len(str, 0); + return str; } - rb_str_set_len(str, 0); - if (ilen == 0) - return str; VALUE io = rb_attr_get(self, id_i_io); - rb_str_locktmp(str); for (;;) { + rb_str_locktmp(str); int nread = SSL_read(ssl, RSTRING_PTR(str), ilen); + rb_str_unlocktmp(str); + + cb_state = rb_attr_get(self, ID_callback_state); + if (!NIL_P(cb_state)) { + rb_ivar_set(self, ID_callback_state, Qnil); + ossl_clear_error(); + rb_jump_tag(NUM2INT(cb_state)); + } + switch (ssl_get_error(ssl, nread)) { case SSL_ERROR_NONE: - rb_str_unlocktmp(str); rb_str_set_len(str, nread); return str; case SSL_ERROR_ZERO_RETURN: - rb_str_unlocktmp(str); if (no_exception_p(opts)) { return Qnil; } rb_eof_error(); case SSL_ERROR_WANT_WRITE: if (nonblock) { - rb_str_unlocktmp(str); if (no_exception_p(opts)) { return sym_wait_writable; } write_would_block(nonblock); } io_wait_writable(io); - continue; + break; case SSL_ERROR_WANT_READ: if (nonblock) { - rb_str_unlocktmp(str); if (no_exception_p(opts)) { return sym_wait_readable; } read_would_block(nonblock); } io_wait_readable(io); - continue; + break; case SSL_ERROR_SYSCALL: if (!ERR_peek_error()) { - rb_str_unlocktmp(str); if (errno) rb_sys_fail(0); else { @@ -2011,9 +2022,13 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) } /* fall through */ default: - rb_str_unlocktmp(str); ossl_raise(eSSLError, "SSL_read"); } + + // Ensure the buffer is not modified during io_wait_*able() + rb_str_modify(str); + if (rb_str_capacity(str) < (size_t)ilen) + rb_raise(eSSLError, "read buffer was modified"); } } @@ -2051,28 +2066,40 @@ ossl_ssl_read_nonblock(int argc, VALUE *argv, VALUE self) } static VALUE -ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts) +ossl_ssl_write_internal_safe(VALUE _args) { + VALUE *args = (VALUE*)_args; + VALUE self = args[0]; + VALUE str = args[1]; + VALUE opts = args[2]; + SSL *ssl; rb_io_t *fptr; int num, nonblock = opts != Qfalse; - VALUE tmp; + VALUE cb_state; GetSSL(self, ssl); if (!ssl_started(ssl)) rb_raise(eSSLError, "SSL session is not started yet"); - tmp = rb_str_new_frozen(StringValue(str)); VALUE io = rb_attr_get(self, id_i_io); GetOpenFile(io, fptr); /* SSL_write(3ssl) manpage states num == 0 is undefined */ - num = RSTRING_LENINT(tmp); + num = RSTRING_LENINT(str); if (num == 0) return INT2FIX(0); for (;;) { - int nwritten = SSL_write(ssl, RSTRING_PTR(tmp), num); + int nwritten = SSL_write(ssl, RSTRING_PTR(str), num); + + cb_state = rb_attr_get(self, ID_callback_state); + if (!NIL_P(cb_state)) { + rb_ivar_set(self, ID_callback_state, Qnil); + ossl_clear_error(); + rb_jump_tag(NUM2INT(cb_state)); + } + switch (ssl_get_error(ssl, nwritten)) { case SSL_ERROR_NONE: return INT2NUM(nwritten); @@ -2105,6 +2132,28 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts) } } + +static VALUE +ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts) +{ + StringValue(str); + int frozen = RB_OBJ_FROZEN(str); + if (!frozen) { + rb_str_locktmp(str); + } + int state; + VALUE args[3] = {self, str, opts}; + VALUE result = rb_protect(ossl_ssl_write_internal_safe, (VALUE)args, &state); + if (!frozen) { + rb_str_unlocktmp(str); + } + + if (state) { + rb_jump_tag(state); + } + return result; +} + /* * call-seq: * ssl.syswrite(string) => Integer @@ -2149,12 +2198,12 @@ ossl_ssl_stop(VALUE self) GetSSL(self, ssl); if (!ssl_started(ssl)) - return Qnil; + return Qnil; ret = SSL_shutdown(ssl); if (ret == 1) /* Have already received close_notify */ - return Qnil; + return Qnil; if (ret == 0) /* Sent close_notify, but we don't wait for reply */ - return Qnil; + return Qnil; /* * XXX: Something happened. Possibly it failed because the underlying socket @@ -2240,20 +2289,20 @@ ossl_ssl_get_peer_cert_chain(VALUE self) num = sk_X509_num(chain); ary = rb_ary_new2(num); for (i = 0; i < num; i++){ - cert = sk_X509_value(chain, i); - rb_ary_push(ary, ossl_x509_new(cert)); + cert = sk_X509_value(chain, i); + rb_ary_push(ary, ossl_x509_new(cert)); } return ary; } /* -* call-seq: -* ssl.ssl_version => String -* -* Returns a String representing the SSL/TLS version that was negotiated -* for the connection, for example "TLSv1.2". -*/ + * call-seq: + * ssl.ssl_version => String + * + * Returns a String representing the SSL/TLS version that was negotiated + * for the connection, for example "TLSv1.2". + */ static VALUE ossl_ssl_get_version(VALUE self) { @@ -2374,10 +2423,10 @@ ossl_ssl_set_hostname(VALUE self, VALUE arg) GetSSL(self, ssl); if (!NIL_P(arg)) - hostname = StringValueCStr(arg); + hostname = StringValueCStr(arg); if (!SSL_set_tlsext_host_name(ssl, hostname)) - ossl_raise(eSSLError, NULL); + ossl_raise(eSSLError, NULL); /* for SSLSocket#hostname */ rb_ivar_set(self, id_i_hostname, arg); @@ -2456,7 +2505,7 @@ ossl_ssl_get_peer_finished(VALUE self) /* * call-seq: - * ssl.client_ca => [x509name, ...] + * ssl.client_ca => [x509name, ...] or nil * * Returns the list of client CAs. Please note that in contrast to * SSLContext#client_ca= no array of X509::Certificate is returned but @@ -2474,6 +2523,8 @@ ossl_ssl_get_client_ca_list(VALUE self) GetSSL(self, ssl); ca = SSL_get_client_CA_list(ssl); + if (!ca) + return Qnil; return ossl_x509name_sk2ary(ca); } @@ -2496,9 +2547,9 @@ ossl_ssl_npn_protocol(VALUE self) SSL_get0_next_proto_negotiated(ssl, &out, &outlen); if (!outlen) - return Qnil; + return Qnil; else - return rb_str_new((const char *) out, outlen); + return rb_str_new((const char *) out, outlen); } # endif @@ -2520,9 +2571,9 @@ ossl_ssl_alpn_protocol(VALUE self) SSL_get0_alpn_selected(ssl, &out, &outlen); if (!outlen) - return Qnil; + return Qnil; else - return rb_str_new((const char *) out, outlen); + return rb_str_new((const char *) out, outlen); } /* @@ -2555,15 +2606,15 @@ ossl_ssl_export_keying_material(int argc, VALUE *argv, VALUE self) str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (!NIL_P(context)) { - use_ctx = 1; - StringValue(context); - ctx = (unsigned char *)RSTRING_PTR(context); - ctx_len = RSTRING_LEN(context); + use_ctx = 1; + StringValue(context); + ctx = (unsigned char *)RSTRING_PTR(context); + ctx_len = RSTRING_LEN(context); } ret = SSL_export_keying_material(ssl, p, len, (char *)RSTRING_PTR(label), - RSTRING_LENINT(label), ctx, ctx_len, use_ctx); + RSTRING_LENINT(label), ctx, ctx_len, use_ctx); if (ret == 0 || ret == -1) { - ossl_raise(eSSLError, "SSL_export_keying_material"); + ossl_raise(eSSLError, "SSL_export_keying_material"); } return str; } @@ -2582,17 +2633,77 @@ ossl_ssl_tmp_key(VALUE self) GetSSL(self, ssl); if (!SSL_get_server_tmp_key(ssl, &key)) - return Qnil; - return ossl_pkey_new(key); + return Qnil; + return ossl_pkey_wrap(key); +} + +#ifdef HAVE_SSL_GET0_PEER_SIGNATURE_NAME +/* + * call-seq: + * ssl.sigalg => String or nil + * + * Returns the signature algorithm name, the IANA name of the signature scheme + * used by the local to sign the TLS handshake. + */ +static VALUE +ossl_ssl_get_sigalg(VALUE self) +{ + SSL *ssl; + const char *name; + + GetSSL(self, ssl); + if (!SSL_get0_signature_name(ssl, &name)) + return Qnil; + return rb_str_new_cstr(name); +} + +/* + * call-seq: + * ssl.peer_sigalg => String or nil + * + * Returns the signature algorithm name, the IANA name of the signature scheme + * used by the peer to sign the TLS handshake. + */ +static VALUE +ossl_ssl_get_peer_sigalg(VALUE self) +{ + SSL *ssl; + const char *name; + + GetSSL(self, ssl); + if (!SSL_get0_peer_signature_name(ssl, &name)) + return Qnil; + return rb_str_new_cstr(name); +} +#endif + +#ifdef HAVE_SSL_GET0_GROUP_NAME +/* + * call-seq: + * ssl.group => String or nil + * + * Returns the name of the group that was used for the key agreement of the + * current TLS session establishment. + */ +static VALUE +ossl_ssl_get_group(VALUE self) +{ + SSL *ssl; + const char *name; + + GetSSL(self, ssl); + if (!(name = SSL_get0_group_name(ssl))) + return Qnil; + return rb_str_new_cstr(name); } +#endif + #endif /* !defined(OPENSSL_NO_SOCK) */ void Init_ossl_ssl(void) { #if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); rb_mWaitReadable = rb_define_module_under(rb_cIO, "WaitReadable"); rb_mWaitWritable = rb_define_module_under(rb_cIO, "WaitWritable"); #endif @@ -2601,15 +2712,12 @@ Init_ossl_ssl(void) id_call = rb_intern_const("call"); ID_callback_state = rb_intern_const("callback_state"); - ossl_ssl_ex_vcb_idx = SSL_get_ex_new_index(0, (void *)"ossl_ssl_ex_vcb_idx", 0, 0, 0); - if (ossl_ssl_ex_vcb_idx < 0) - ossl_raise(rb_eRuntimeError, "SSL_get_ex_new_index"); ossl_ssl_ex_ptr_idx = SSL_get_ex_new_index(0, (void *)"ossl_ssl_ex_ptr_idx", 0, 0, 0); if (ossl_ssl_ex_ptr_idx < 0) - ossl_raise(rb_eRuntimeError, "SSL_get_ex_new_index"); + ossl_raise(rb_eRuntimeError, "SSL_get_ex_new_index"); ossl_sslctx_ex_ptr_idx = SSL_CTX_get_ex_new_index(0, (void *)"ossl_sslctx_ex_ptr_idx", 0, 0, 0); if (ossl_sslctx_ex_ptr_idx < 0) - ossl_raise(rb_eRuntimeError, "SSL_CTX_get_ex_new_index"); + ossl_raise(rb_eRuntimeError, "SSL_CTX_get_ex_new_index"); /* Document-module: OpenSSL::SSL * @@ -2747,6 +2855,23 @@ Init_ossl_ssl(void) */ rb_attr(cSSLContext, rb_intern_const("client_cert_cb"), 1, 1, Qfalse); +#ifndef OPENSSL_NO_DH + /* + * A callback invoked when DH parameters are required for ephemeral DH key + * exchange. + * + * The callback is invoked with the SSLSocket, a + * flag indicating the use of an export cipher and the keylength + * required. + * + * The callback must return an OpenSSL::PKey::DH instance of the correct + * key length. + * + * <b>Deprecated in version 3.0.</b> Use #tmp_dh= instead. + */ + rb_attr(cSSLContext, rb_intern_const("tmp_dh_callback"), 1, 1, Qfalse); +#endif + /* * Sets the context in which a session can be reused. This allows * sessions for multiple applications to be distinguished, for example, by @@ -2889,17 +3014,22 @@ Init_ossl_ssl(void) rb_define_alias(cSSLContext, "ssl_timeout", "timeout"); rb_define_alias(cSSLContext, "ssl_timeout=", "timeout="); - rb_define_private_method(cSSLContext, "set_minmax_proto_version", - ossl_sslctx_set_minmax_proto_version, 2); + rb_define_method(cSSLContext, "min_version=", ossl_sslctx_set_min_version, 1); + rb_define_method(cSSLContext, "max_version=", ossl_sslctx_set_max_version, 1); rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0); rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 1); -#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES rb_define_method(cSSLContext, "ciphersuites=", ossl_sslctx_set_ciphersuites, 1); +#ifdef HAVE_SSL_CTX_SET1_SIGALGS_LIST // Not in LibreSSL yet + rb_define_method(cSSLContext, "sigalgs=", ossl_sslctx_set_sigalgs, 1); +#endif +#ifdef HAVE_SSL_CTX_SET1_CLIENT_SIGALGS_LIST // Not in LibreSSL or AWS-LC yet + rb_define_method(cSSLContext, "client_sigalgs=", ossl_sslctx_set_client_sigalgs, 1); #endif #ifndef OPENSSL_NO_DH rb_define_method(cSSLContext, "tmp_dh=", ossl_sslctx_set_tmp_dh, 1); #endif - rb_define_method(cSSLContext, "ecdh_curves=", ossl_sslctx_set_ecdh_curves, 1); + rb_define_method(cSSLContext, "groups=", ossl_sslctx_set_groups, 1); + rb_define_alias(cSSLContext, "ecdh_curves=", "groups="); rb_define_method(cSSLContext, "security_level", ossl_sslctx_get_security_level, 0); rb_define_method(cSSLContext, "security_level=", ossl_sslctx_set_security_level, 1); #ifdef SSL_MODE_SEND_FALLBACK_SCSV @@ -3006,6 +3136,13 @@ Init_ossl_ssl(void) # ifdef OSSL_USE_NEXTPROTONEG rb_define_method(cSSLSocket, "npn_protocol", ossl_ssl_npn_protocol, 0); # endif +#ifdef HAVE_SSL_GET0_PEER_SIGNATURE_NAME + rb_define_method(cSSLSocket, "sigalg", ossl_ssl_get_sigalg, 0); + rb_define_method(cSSLSocket, "peer_sigalg", ossl_ssl_get_peer_sigalg, 0); +#endif +#ifdef HAVE_SSL_GET0_GROUP_NAME + rb_define_method(cSSLSocket, "group", ossl_ssl_get_group, 0); +#endif rb_define_const(mSSL, "VERIFY_NONE", INT2NUM(SSL_VERIFY_NONE)); rb_define_const(mSSL, "VERIFY_PEER", INT2NUM(SSL_VERIFY_PEER)); @@ -3031,7 +3168,7 @@ Init_ossl_ssl(void) #ifdef SSL_OP_DISABLE_TLSEXT_CA_NAMES /* OpenSSL 3.0 */ rb_define_const(mSSL, "OP_DISABLE_TLSEXT_CA_NAMES", ULONG2NUM(SSL_OP_DISABLE_TLSEXT_CA_NAMES)); #endif -#ifdef SSL_OP_ALLOW_NO_DHE_KEX /* OpenSSL 1.1.1 */ +#ifdef SSL_OP_ALLOW_NO_DHE_KEX /* OpenSSL 1.1.1, missing in LibreSSL */ rb_define_const(mSSL, "OP_ALLOW_NO_DHE_KEX", ULONG2NUM(SSL_OP_ALLOW_NO_DHE_KEX)); #endif rb_define_const(mSSL, "OP_DONT_INSERT_EMPTY_FRAGMENTS", ULONG2NUM(SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS)); @@ -3039,28 +3176,26 @@ Init_ossl_ssl(void) rb_define_const(mSSL, "OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION", ULONG2NUM(SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION)); rb_define_const(mSSL, "OP_NO_COMPRESSION", ULONG2NUM(SSL_OP_NO_COMPRESSION)); rb_define_const(mSSL, "OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION", ULONG2NUM(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)); -#ifdef SSL_OP_NO_ENCRYPT_THEN_MAC /* OpenSSL 1.1.1 */ +#ifdef SSL_OP_NO_ENCRYPT_THEN_MAC /* OpenSSL 1.1.1, missing in LibreSSL */ rb_define_const(mSSL, "OP_NO_ENCRYPT_THEN_MAC", ULONG2NUM(SSL_OP_NO_ENCRYPT_THEN_MAC)); #endif -#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT /* OpenSSL 1.1.1 */ +#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT /* OpenSSL 1.1.1, missing in LibreSSL */ rb_define_const(mSSL, "OP_ENABLE_MIDDLEBOX_COMPAT", ULONG2NUM(SSL_OP_ENABLE_MIDDLEBOX_COMPAT)); #endif -#ifdef SSL_OP_PRIORITIZE_CHACHA /* OpenSSL 1.1.1 */ +#ifdef SSL_OP_PRIORITIZE_CHACHA /* OpenSSL 1.1.1, missing in LibreSSL */ rb_define_const(mSSL, "OP_PRIORITIZE_CHACHA", ULONG2NUM(SSL_OP_PRIORITIZE_CHACHA)); #endif -#ifdef SSL_OP_NO_ANTI_REPLAY /* OpenSSL 1.1.1 */ +#ifdef SSL_OP_NO_ANTI_REPLAY /* OpenSSL 1.1.1, missing in LibreSSL */ rb_define_const(mSSL, "OP_NO_ANTI_REPLAY", ULONG2NUM(SSL_OP_NO_ANTI_REPLAY)); #endif rb_define_const(mSSL, "OP_NO_SSLv3", ULONG2NUM(SSL_OP_NO_SSLv3)); rb_define_const(mSSL, "OP_NO_TLSv1", ULONG2NUM(SSL_OP_NO_TLSv1)); rb_define_const(mSSL, "OP_NO_TLSv1_1", ULONG2NUM(SSL_OP_NO_TLSv1_1)); rb_define_const(mSSL, "OP_NO_TLSv1_2", ULONG2NUM(SSL_OP_NO_TLSv1_2)); -#ifdef SSL_OP_NO_TLSv1_3 /* OpenSSL 1.1.1 */ rb_define_const(mSSL, "OP_NO_TLSv1_3", ULONG2NUM(SSL_OP_NO_TLSv1_3)); -#endif rb_define_const(mSSL, "OP_CIPHER_SERVER_PREFERENCE", ULONG2NUM(SSL_OP_CIPHER_SERVER_PREFERENCE)); rb_define_const(mSSL, "OP_TLS_ROLLBACK_BUG", ULONG2NUM(SSL_OP_TLS_ROLLBACK_BUG)); -#ifdef SSL_OP_NO_RENEGOTIATION /* OpenSSL 1.1.1 */ +#ifdef SSL_OP_NO_RENEGOTIATION /* OpenSSL 1.1.1, missing in LibreSSL */ rb_define_const(mSSL, "OP_NO_RENEGOTIATION", ULONG2NUM(SSL_OP_NO_RENEGOTIATION)); #endif rb_define_const(mSSL, "OP_CRYPTOPRO_TLSEXT_BUG", ULONG2NUM(SSL_OP_CRYPTOPRO_TLSEXT_BUG)); @@ -3122,17 +3257,14 @@ Init_ossl_ssl(void) rb_define_const(mSSL, "TLS1_1_VERSION", INT2NUM(TLS1_1_VERSION)); /* TLS 1.2 */ rb_define_const(mSSL, "TLS1_2_VERSION", INT2NUM(TLS1_2_VERSION)); -#ifdef TLS1_3_VERSION /* OpenSSL 1.1.1 */ /* TLS 1.3 */ rb_define_const(mSSL, "TLS1_3_VERSION", INT2NUM(TLS1_3_VERSION)); -#endif sym_exception = ID2SYM(rb_intern_const("exception")); sym_wait_readable = ID2SYM(rb_intern_const("wait_readable")); sym_wait_writable = ID2SYM(rb_intern_const("wait_writable")); - id_tmp_dh_callback = rb_intern_const("tmp_dh_callback"); id_npn_protocols_encoded = rb_intern_const("npn_protocols_encoded"); id_each = rb_intern_const("each"); @@ -3163,6 +3295,7 @@ Init_ossl_ssl(void) DefIVarID(servername_cb); DefIVarID(verify_hostname); DefIVarID(keylog_cb); + DefIVarID(tmp_dh_callback); DefIVarID(io); DefIVarID(context); diff --git a/ext/openssl/ossl_ssl.h b/ext/openssl/ossl_ssl.h index 535c56097c..a87e62d450 100644 --- a/ext/openssl/ossl_ssl.h +++ b/ext/openssl/ossl_ssl.h @@ -5,23 +5,23 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(_OSSL_SSL_H_) #define _OSSL_SSL_H_ #define GetSSL(obj, ssl) do { \ - TypedData_Get_Struct((obj), SSL, &ossl_ssl_type, (ssl)); \ - if (!(ssl)) { \ - ossl_raise(rb_eRuntimeError, "SSL is not initialized"); \ - } \ + TypedData_Get_Struct((obj), SSL, &ossl_ssl_type, (ssl)); \ + if (!(ssl)) { \ + ossl_raise(rb_eRuntimeError, "SSL is not initialized"); \ + } \ } while (0) #define GetSSLSession(obj, sess) do { \ - TypedData_Get_Struct((obj), SSL_SESSION, &ossl_ssl_session_type, (sess)); \ - if (!(sess)) { \ - ossl_raise(rb_eRuntimeError, "SSL Session wasn't initialized."); \ - } \ + TypedData_Get_Struct((obj), SSL_SESSION, &ossl_ssl_session_type, (sess)); \ + if (!(sess)) { \ + ossl_raise(rb_eRuntimeError, "SSL Session wasn't initialized."); \ + } \ } while (0) extern const rb_data_type_t ossl_ssl_type; diff --git a/ext/openssl/ossl_ssl_session.c b/ext/openssl/ossl_ssl_session.c index c5df902c60..8a2fbf4100 100644 --- a/ext/openssl/ossl_ssl_session.c +++ b/ext/openssl/ossl_ssl_session.c @@ -17,14 +17,14 @@ ossl_ssl_session_free(void *ptr) const rb_data_type_t ossl_ssl_session_type = { "OpenSSL/SSL/Session", { - 0, ossl_ssl_session_free, + 0, ossl_ssl_session_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; static VALUE ossl_ssl_session_alloc(VALUE klass) { - return TypedData_Wrap_Struct(klass, &ossl_ssl_session_type, NULL); + return TypedData_Wrap_Struct(klass, &ossl_ssl_session_type, NULL); } /* @@ -69,6 +69,7 @@ ossl_ssl_session_initialize(VALUE self, VALUE arg1) return self; } +/* :nodoc: */ static VALUE ossl_ssl_session_initialize_copy(VALUE self, VALUE other) { @@ -79,9 +80,9 @@ ossl_ssl_session_initialize_copy(VALUE self, VALUE other) GetSSLSession(other, sess_other); sess_new = ASN1_dup((i2d_of_void *)i2d_SSL_SESSION, (d2i_of_void *)d2i_SSL_SESSION, - (char *)sess_other); + (char *)sess_other); if (!sess_new) - ossl_raise(eSSLSession, "ASN1_dup"); + ossl_raise(eSSLSession, "ASN1_dup"); RTYPEDDATA_DATA(self) = sess_new; SSL_SESSION_free(sess); @@ -98,9 +99,9 @@ ossl_SSL_SESSION_cmp(const SSL_SESSION *a, const SSL_SESSION *b) const unsigned char *b_sid = SSL_SESSION_get_id(b, &b_len); if (SSL_SESSION_get_protocol_version(a) != SSL_SESSION_get_protocol_version(b)) - return 1; + return 1; if (a_len != b_len) - return 1; + return 1; return CRYPTO_memcmp(a_sid, b_sid, a_len); } @@ -113,15 +114,15 @@ ossl_SSL_SESSION_cmp(const SSL_SESSION *a, const SSL_SESSION *b) */ static VALUE ossl_ssl_session_eq(VALUE val1, VALUE val2) { - SSL_SESSION *ctx1, *ctx2; + SSL_SESSION *ctx1, *ctx2; - GetSSLSession(val1, ctx1); - GetSSLSession(val2, ctx2); + GetSSLSession(val1, ctx1); + GetSSLSession(val2, ctx2); - switch (ossl_SSL_SESSION_cmp(ctx1, ctx2)) { - case 0: return Qtrue; - default: return Qfalse; - } + switch (ossl_SSL_SESSION_cmp(ctx1, ctx2)) { + case 0: return Qtrue; + default: return Qfalse; + } } /* @@ -139,7 +140,7 @@ ossl_ssl_session_get_time(VALUE self) GetSSLSession(self, ctx); t = SSL_SESSION_get_time(ctx); if (t == 0) - return Qnil; + return Qnil; return rb_funcall(rb_cTime, rb_intern("at"), 1, LONG2NUM(t)); } @@ -174,16 +175,16 @@ ossl_ssl_session_get_timeout(VALUE self) */ static VALUE ossl_ssl_session_set_time(VALUE self, VALUE time_v) { - SSL_SESSION *ctx; - long t; - - GetSSLSession(self, ctx); - if (rb_obj_is_instance_of(time_v, rb_cTime)) { - time_v = rb_funcall(time_v, rb_intern("to_i"), 0); - } - t = NUM2LONG(time_v); - SSL_SESSION_set_time(ctx, t); - return ossl_ssl_session_get_time(self); + SSL_SESSION *ctx; + long t; + + GetSSLSession(self, ctx); + if (rb_obj_is_instance_of(time_v, rb_cTime)) { + time_v = rb_funcall(time_v, rb_intern("to_i"), 0); + } + t = NUM2LONG(time_v); + SSL_SESSION_set_time(ctx, t); + return ossl_ssl_session_get_time(self); } /* @@ -194,13 +195,13 @@ static VALUE ossl_ssl_session_set_time(VALUE self, VALUE time_v) */ static VALUE ossl_ssl_session_set_timeout(VALUE self, VALUE time_v) { - SSL_SESSION *ctx; - long t; + SSL_SESSION *ctx; + long t; - GetSSLSession(self, ctx); - t = NUM2LONG(time_v); - SSL_SESSION_set_timeout(ctx, t); - return ossl_ssl_session_get_timeout(self); + GetSSLSession(self, ctx); + t = NUM2LONG(time_v); + SSL_SESSION_set_timeout(ctx, t); + return ossl_ssl_session_get_timeout(self); } /* @@ -208,18 +209,18 @@ static VALUE ossl_ssl_session_set_timeout(VALUE self, VALUE time_v) * session.id -> String * * Returns the Session ID. -*/ + */ static VALUE ossl_ssl_session_get_id(VALUE self) { - SSL_SESSION *ctx; - const unsigned char *p = NULL; - unsigned int i = 0; + SSL_SESSION *ctx; + const unsigned char *p = NULL; + unsigned int i = 0; - GetSSLSession(self, ctx); + GetSSLSession(self, ctx); - p = SSL_SESSION_get_id(ctx, &i); + p = SSL_SESSION_get_id(ctx, &i); - return rb_str_new((const char *) p, i); + return rb_str_new((const char *) p, i); } /* @@ -230,22 +231,22 @@ static VALUE ossl_ssl_session_get_id(VALUE self) */ static VALUE ossl_ssl_session_to_der(VALUE self) { - SSL_SESSION *ctx; - unsigned char *p; - int len; - VALUE str; - - GetSSLSession(self, ctx); - len = i2d_SSL_SESSION(ctx, NULL); - if (len <= 0) { - ossl_raise(eSSLSession, "i2d_SSL_SESSION"); - } - - str = rb_str_new(0, len); - p = (unsigned char *)RSTRING_PTR(str); - i2d_SSL_SESSION(ctx, &p); - ossl_str_adjust(str, p); - return str; + SSL_SESSION *ctx; + unsigned char *p; + int len; + VALUE str; + + GetSSLSession(self, ctx); + len = i2d_SSL_SESSION(ctx, NULL); + if (len <= 0) { + ossl_raise(eSSLSession, "i2d_SSL_SESSION"); + } + + str = rb_str_new(0, len); + p = (unsigned char *)RSTRING_PTR(str); + i2d_SSL_SESSION(ctx, &p); + ossl_str_adjust(str, p); + return str; } /* @@ -256,22 +257,22 @@ static VALUE ossl_ssl_session_to_der(VALUE self) */ static VALUE ossl_ssl_session_to_pem(VALUE self) { - SSL_SESSION *ctx; - BIO *out; + SSL_SESSION *ctx; + BIO *out; - GetSSLSession(self, ctx); + GetSSLSession(self, ctx); - if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eSSLSession, "BIO_s_mem()"); - } + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eSSLSession, "BIO_s_mem()"); + } - if (!PEM_write_bio_SSL_SESSION(out, ctx)) { - BIO_free(out); - ossl_raise(eSSLSession, "SSL_SESSION_print()"); - } + if (!PEM_write_bio_SSL_SESSION(out, ctx)) { + BIO_free(out); + ossl_raise(eSSLSession, "SSL_SESSION_print()"); + } - return ossl_membio2str(out); + return ossl_membio2str(out); } @@ -283,49 +284,44 @@ static VALUE ossl_ssl_session_to_pem(VALUE self) */ static VALUE ossl_ssl_session_to_text(VALUE self) { - SSL_SESSION *ctx; - BIO *out; + SSL_SESSION *ctx; + BIO *out; - GetSSLSession(self, ctx); + GetSSLSession(self, ctx); - if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eSSLSession, "BIO_s_mem()"); - } + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eSSLSession, "BIO_s_mem()"); + } - if (!SSL_SESSION_print(out, ctx)) { - BIO_free(out); - ossl_raise(eSSLSession, "SSL_SESSION_print()"); - } + if (!SSL_SESSION_print(out, ctx)) { + BIO_free(out); + ossl_raise(eSSLSession, "SSL_SESSION_print()"); + } - return ossl_membio2str(out); + return ossl_membio2str(out); } #endif /* !defined(OPENSSL_NO_SOCK) */ void Init_ossl_ssl_session(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - mSSL = rb_define_module_under(mOSSL, "SSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif #ifndef OPENSSL_NO_SOCK - cSSLSession = rb_define_class_under(mSSL, "Session", rb_cObject); - eSSLSession = rb_define_class_under(cSSLSession, "SessionError", eOSSLError); - - rb_define_alloc_func(cSSLSession, ossl_ssl_session_alloc); - rb_define_method(cSSLSession, "initialize", ossl_ssl_session_initialize, 1); - rb_define_method(cSSLSession, "initialize_copy", ossl_ssl_session_initialize_copy, 1); - - rb_define_method(cSSLSession, "==", ossl_ssl_session_eq, 1); - - rb_define_method(cSSLSession, "time", ossl_ssl_session_get_time, 0); - rb_define_method(cSSLSession, "time=", ossl_ssl_session_set_time, 1); - rb_define_method(cSSLSession, "timeout", ossl_ssl_session_get_timeout, 0); - rb_define_method(cSSLSession, "timeout=", ossl_ssl_session_set_timeout, 1); - rb_define_method(cSSLSession, "id", ossl_ssl_session_get_id, 0); - rb_define_method(cSSLSession, "to_der", ossl_ssl_session_to_der, 0); - rb_define_method(cSSLSession, "to_pem", ossl_ssl_session_to_pem, 0); - rb_define_method(cSSLSession, "to_text", ossl_ssl_session_to_text, 0); + cSSLSession = rb_define_class_under(mSSL, "Session", rb_cObject); + eSSLSession = rb_define_class_under(cSSLSession, "SessionError", eOSSLError); + + rb_define_alloc_func(cSSLSession, ossl_ssl_session_alloc); + rb_define_method(cSSLSession, "initialize", ossl_ssl_session_initialize, 1); + rb_define_method(cSSLSession, "initialize_copy", ossl_ssl_session_initialize_copy, 1); + + rb_define_method(cSSLSession, "==", ossl_ssl_session_eq, 1); + + rb_define_method(cSSLSession, "time", ossl_ssl_session_get_time, 0); + rb_define_method(cSSLSession, "time=", ossl_ssl_session_set_time, 1); + rb_define_method(cSSLSession, "timeout", ossl_ssl_session_get_timeout, 0); + rb_define_method(cSSLSession, "timeout=", ossl_ssl_session_set_timeout, 1); + rb_define_method(cSSLSession, "id", ossl_ssl_session_get_id, 0); + rb_define_method(cSSLSession, "to_der", ossl_ssl_session_to_der, 0); + rb_define_method(cSSLSession, "to_pem", ossl_ssl_session_to_pem, 0); + rb_define_method(cSSLSession, "to_text", ossl_ssl_session_to_text, 0); #endif /* !defined(OPENSSL_NO_SOCK) */ } diff --git a/ext/openssl/ossl_ts.c b/ext/openssl/ossl_ts.c index f698bdc7ff..b31a854a63 100644 --- a/ext/openssl/ossl_ts.c +++ b/ext/openssl/ossl_ts.c @@ -5,7 +5,7 @@ */ /* * This program is licenced under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -103,7 +103,7 @@ static const rb_data_type_t ossl_ts_resp_type = { static void ossl_ts_token_info_free(void *ptr) { - TS_TST_INFO_free(ptr); + TS_TST_INFO_free(ptr); } static const rb_data_type_t ossl_ts_token_info_type = { @@ -132,41 +132,10 @@ asn1_to_der(void *template, int (*i2d)(void *template, unsigned char **pp)) return str; } -static ASN1_OBJECT* -obj_to_asn1obj(VALUE obj) -{ - ASN1_OBJECT *a1obj; - - StringValue(obj); - a1obj = OBJ_txt2obj(RSTRING_PTR(obj), 0); - if(!a1obj) a1obj = OBJ_txt2obj(RSTRING_PTR(obj), 1); - if(!a1obj) ossl_raise(eASN1Error, "invalid OBJECT ID"); - - return a1obj; -} - static VALUE obj_to_asn1obj_i(VALUE obj) { - return (VALUE)obj_to_asn1obj(obj); -} - -static VALUE -get_asn1obj(ASN1_OBJECT *obj) -{ - BIO *out; - VALUE ret; - int nid; - if ((nid = OBJ_obj2nid(obj)) != NID_undef) - ret = rb_str_new2(OBJ_nid2sn(nid)); - else{ - if (!(out = BIO_new(BIO_s_mem()))) - ossl_raise(eX509AttrError, NULL); - i2a_ASN1_OBJECT(out, obj); - ret = ossl_membio2str(out); - } - - return ret; + return (VALUE)ossl_to_asn1obj(obj); } static VALUE @@ -233,11 +202,13 @@ ossl_ts_req_get_algorithm(VALUE self) TS_REQ *req; TS_MSG_IMPRINT *mi; X509_ALGOR *algor; + const ASN1_OBJECT *obj; GetTSRequest(self, req); mi = TS_REQ_get_msg_imprint(req); algor = TS_MSG_IMPRINT_get_algo(mi); - return get_asn1obj(algor->algorithm); + X509_ALGOR_get0(&obj, NULL, NULL, algor); + return ossl_asn1obj_to_string(obj); } /* @@ -259,7 +230,7 @@ ossl_ts_req_set_algorithm(VALUE self, VALUE algo) X509_ALGOR *algor; GetTSRequest(self, req); - obj = obj_to_asn1obj(algo); + obj = ossl_to_asn1obj(algo); mi = TS_REQ_get_msg_imprint(req); algor = TS_MSG_IMPRINT_get_algo(mi); if (!X509_ALGOR_set0(algor, obj, V_ASN1_NULL, NULL)) { @@ -288,7 +259,7 @@ ossl_ts_req_get_msg_imprint(VALUE self) mi = TS_REQ_get_msg_imprint(req); hashed_msg = TS_MSG_IMPRINT_get_msg(mi); - ret = rb_str_new((const char *)hashed_msg->data, hashed_msg->length); + ret = asn1str_to_str(hashed_msg); return ret; } @@ -366,7 +337,7 @@ ossl_ts_req_get_policy_id(VALUE self) GetTSRequest(self, req); if (!TS_REQ_get_policy_id(req)) return Qnil; - return get_asn1obj(TS_REQ_get_policy_id(req)); + return ossl_asn1obj_to_string(TS_REQ_get_policy_id(req)); } /* @@ -389,7 +360,7 @@ ossl_ts_req_set_policy_id(VALUE self, VALUE oid) int ok; GetTSRequest(self, req); - obj = obj_to_asn1obj(oid); + obj = ossl_to_asn1obj(oid); ok = TS_REQ_set_policy_id(req, obj); ASN1_OBJECT_free(obj); if (!ok) @@ -487,23 +458,44 @@ ossl_ts_req_to_der(VALUE self) TS_REQ *req; TS_MSG_IMPRINT *mi; X509_ALGOR *algo; + const ASN1_OBJECT *obj; ASN1_OCTET_STRING *hashed_msg; GetTSRequest(self, req); mi = TS_REQ_get_msg_imprint(req); algo = TS_MSG_IMPRINT_get_algo(mi); - if (OBJ_obj2nid(algo->algorithm) == NID_undef) + X509_ALGOR_get0(&obj, NULL, NULL, algo); + if (OBJ_obj2nid(obj) == NID_undef) ossl_raise(eTimestampError, "Message imprint missing algorithm"); hashed_msg = TS_MSG_IMPRINT_get_msg(mi); - if (!hashed_msg->length) + if (!ASN1_STRING_length(hashed_msg)) ossl_raise(eTimestampError, "Message imprint missing hashed message"); return asn1_to_der((void *)req, (int (*)(void *, unsigned char **))i2d_TS_REQ); } static VALUE +ossl_ts_req_to_text(VALUE self) +{ + TS_REQ *req; + BIO *out; + + GetTSRequest(self, req); + + out = BIO_new(BIO_s_mem()); + if (!out) ossl_raise(eTimestampError, NULL); + + if (!TS_REQ_print_bio(out, req)) { + BIO_free(out); + ossl_raise(eTimestampError, NULL); + } + + return ossl_membio2str(out); +} + +static VALUE ossl_ts_resp_alloc(VALUE klass) { TS_RESP *resp; @@ -598,14 +590,7 @@ ossl_ts_resp_get_failure_info(VALUE self) { TS_RESP *resp; TS_STATUS_INFO *si; - - /* The ASN1_BIT_STRING_get_bit changed from 1.0.0. to 1.1.0, making this - * const. */ - #if defined(HAVE_TS_STATUS_INFO_GET0_FAILURE_INFO) const ASN1_BIT_STRING *fi; - #else - ASN1_BIT_STRING *fi; - #endif GetTSResponse(self, resp); si = TS_RESP_get_status_info(resp); @@ -672,21 +657,12 @@ static VALUE ossl_ts_resp_get_token(VALUE self) { TS_RESP *resp; - PKCS7 *p7, *copy; - VALUE obj; + PKCS7 *p7; GetTSResponse(self, resp); if (!(p7 = TS_RESP_get_token(resp))) return Qnil; - - obj = NewPKCS7(cPKCS7); - - if (!(copy = PKCS7_dup(p7))) - ossl_raise(eTimestampError, NULL); - - SetPKCS7(obj, copy); - - return obj; + return ossl_pkcs7_new(p7); } /* @@ -757,6 +733,25 @@ ossl_ts_resp_to_der(VALUE self) return asn1_to_der((void *)resp, (int (*)(void *, unsigned char **))i2d_TS_RESP); } +static VALUE +ossl_ts_resp_to_text(VALUE self) +{ + TS_RESP *resp; + BIO *out; + + GetTSResponse(self, resp); + + out = BIO_new(BIO_s_mem()); + if (!out) ossl_raise(eTimestampError, NULL); + + if (!TS_RESP_print_bio(out, resp)) { + BIO_free(out); + ossl_raise(eTimestampError, NULL); + } + + return ossl_membio2str(out); +} + /* * Verifies a timestamp token by checking the signature, validating the * certificate chain implied by tsa_certificate and by checking conformance to @@ -826,16 +821,26 @@ ossl_ts_resp_verify(int argc, VALUE *argv, VALUE self) X509_up_ref(cert); } + if (!X509_STORE_up_ref(x509st)) { + sk_X509_pop_free(x509inter, X509_free); + TS_VERIFY_CTX_free(ctx); + ossl_raise(eTimestampError, "X509_STORE_up_ref"); + } + +#ifdef HAVE_TS_VERIFY_CTX_SET0_CERTS + TS_VERIFY_CTX_set0_certs(ctx, x509inter); + TS_VERIFY_CTX_set0_store(ctx, x509st); +#else +# if OSSL_OPENSSL_PREREQ(3, 0, 0) || OSSL_IS_LIBRESSL TS_VERIFY_CTX_set_certs(ctx, x509inter); - TS_VERIFY_CTX_add_flags(ctx, TS_VFY_SIGNATURE); +# else + TS_VERIFY_CTS_set_certs(ctx, x509inter); +# endif TS_VERIFY_CTX_set_store(ctx, x509st); +#endif + TS_VERIFY_CTX_add_flags(ctx, TS_VFY_SIGNATURE); ok = TS_RESP_verify_response(ctx, resp); - /* - * TS_VERIFY_CTX_set_store() call above does not increment the reference - * counter, so it must be unset before TS_VERIFY_CTX_free() is called. - */ - TS_VERIFY_CTX_set_store(ctx, NULL); TS_VERIFY_CTX_free(ctx); if (!ok) @@ -922,7 +927,7 @@ ossl_ts_token_info_get_policy_id(VALUE self) TS_TST_INFO *info; GetTSTokenInfo(self, info); - return get_asn1obj(TS_TST_INFO_get_policy_id(info)); + return ossl_asn1obj_to_string(TS_TST_INFO_get_policy_id(info)); } /* @@ -944,11 +949,13 @@ ossl_ts_token_info_get_algorithm(VALUE self) TS_TST_INFO *info; TS_MSG_IMPRINT *mi; X509_ALGOR *algo; + const ASN1_OBJECT *obj; GetTSTokenInfo(self, info); mi = TS_TST_INFO_get_msg_imprint(info); algo = TS_MSG_IMPRINT_get_algo(mi); - return get_asn1obj(algo->algorithm); + X509_ALGOR_get0(&obj, NULL, NULL, algo); + return ossl_asn1obj_to_string(obj); } /* @@ -974,7 +981,7 @@ ossl_ts_token_info_get_msg_imprint(VALUE self) GetTSTokenInfo(self, info); mi = TS_TST_INFO_get_msg_imprint(info); hashed_msg = TS_MSG_IMPRINT_get_msg(mi); - ret = rb_str_new((const char *)hashed_msg->data, hashed_msg->length); + ret = asn1str_to_str(hashed_msg); return ret; } @@ -1073,6 +1080,25 @@ ossl_ts_token_info_to_der(VALUE self) return asn1_to_der((void *)info, (int (*)(void *, unsigned char **))i2d_TS_TST_INFO); } +static VALUE +ossl_ts_token_info_to_text(VALUE self) +{ + TS_TST_INFO *info; + BIO *out; + + GetTSTokenInfo(self, info); + + out = BIO_new(BIO_s_mem()); + if (!out) ossl_raise(eTimestampError, NULL); + + if (!TS_TST_INFO_print_bio(out, info)) { + BIO_free(out); + ossl_raise(eTimestampError, NULL); + } + + return ossl_membio2str(out); +} + static ASN1_INTEGER * ossl_tsfac_serial_cb(struct TS_resp_ctx *ctx, void *data) { @@ -1095,9 +1121,14 @@ ossl_tsfac_time_cb(struct TS_resp_ctx *ctx, void *data, time_t *sec, long *usec) } static VALUE -ossl_evp_get_digestbyname_i(VALUE arg) +ossl_evp_md_fetch_i(VALUE args_) { - return (VALUE)ossl_evp_get_digestbyname(arg); + VALUE *args = (VALUE *)args_, md_holder; + const EVP_MD *md; + + md = ossl_evp_md_fetch(args[1], &md_holder); + rb_ary_push(args[0], md_holder); + return (VALUE)md; } static VALUE @@ -1133,7 +1164,8 @@ ossl_obj2bio_i(VALUE arg) static VALUE ossl_tsfac_create_ts(VALUE self, VALUE key, VALUE certificate, VALUE request) { - VALUE serial_number, def_policy_id, gen_time, additional_certs, allowed_digests; + VALUE serial_number, def_policy_id, gen_time, additional_certs, + allowed_digests, allowed_digests_tmp = Qnil; VALUE str; STACK_OF(X509) *inter_certs; VALUE tsresp, ret = Qnil; @@ -1194,7 +1226,7 @@ ossl_tsfac_create_ts(VALUE self, VALUE key, VALUE certificate, VALUE request) if (rb_obj_is_kind_of(additional_certs, rb_cArray)) { inter_certs = ossl_protect_x509_ary2sk(additional_certs, &status); if (status) - goto end; + goto end; /* this dups the sk_X509 and ups each cert's ref count */ TS_RESP_CTX_set_certs(ctx, inter_certs); @@ -1210,16 +1242,18 @@ ossl_tsfac_create_ts(VALUE self, VALUE key, VALUE certificate, VALUE request) allowed_digests = ossl_tsfac_get_allowed_digests(self); if (rb_obj_is_kind_of(allowed_digests, rb_cArray)) { - int i; - VALUE rbmd; - const EVP_MD *md; - - for (i = 0; i < RARRAY_LEN(allowed_digests); i++) { - rbmd = rb_ary_entry(allowed_digests, i); - md = (const EVP_MD *)rb_protect(ossl_evp_get_digestbyname_i, rbmd, &status); + allowed_digests_tmp = rb_ary_new_capa(RARRAY_LEN(allowed_digests)); + for (long i = 0; i < RARRAY_LEN(allowed_digests); i++) { + VALUE args[] = { + allowed_digests_tmp, + rb_ary_entry(allowed_digests, i), + }; + const EVP_MD *md = (const EVP_MD *)rb_protect(ossl_evp_md_fetch_i, + (VALUE)args, &status); if (status) goto end; - TS_RESP_CTX_add_md(ctx, md); + if (!TS_RESP_CTX_add_md(ctx, md)) + goto end; } } @@ -1233,6 +1267,7 @@ ossl_tsfac_create_ts(VALUE self, VALUE key, VALUE certificate, VALUE request) response = TS_RESP_create_response(ctx, req_bio); BIO_free(req_bio); + RB_GC_GUARD(allowed_digests_tmp); if (!response) { err_msg = "Error during response generation"; @@ -1246,7 +1281,7 @@ ossl_tsfac_create_ts(VALUE self, VALUE key, VALUE certificate, VALUE request) SetTSResponse(tsresp, response); ret = tsresp; -end: + end: ASN1_INTEGER_free(asn1_serial); ASN1_OBJECT_free(def_policy_id_obj); TS_RESP_CTX_free(ctx); @@ -1263,10 +1298,6 @@ end: void Init_ossl_ts(void) { - #if 0 - mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ - #endif - /* * Possible return value for +Response#failure_info+. Indicates that the * timestamp server rejects the message imprint algorithm used in the @@ -1356,6 +1387,7 @@ Init_ossl_ts(void) rb_define_method(cTimestampResponse, "token_info", ossl_ts_resp_get_token_info, 0); rb_define_method(cTimestampResponse, "tsa_certificate", ossl_ts_resp_get_tsa_certificate, 0); rb_define_method(cTimestampResponse, "to_der", ossl_ts_resp_to_der, 0); + rb_define_method(cTimestampResponse, "to_text", ossl_ts_resp_to_text, 0); rb_define_method(cTimestampResponse, "verify", ossl_ts_resp_verify, -1); /* Document-class: OpenSSL::Timestamp::TokenInfo @@ -1374,6 +1406,7 @@ Init_ossl_ts(void) rb_define_method(cTimestampTokenInfo, "ordering", ossl_ts_token_info_get_ordering, 0); rb_define_method(cTimestampTokenInfo, "nonce", ossl_ts_token_info_get_nonce, 0); rb_define_method(cTimestampTokenInfo, "to_der", ossl_ts_token_info_to_der, 0); + rb_define_method(cTimestampTokenInfo, "to_text", ossl_ts_token_info_to_text, 0); /* Document-class: OpenSSL::Timestamp::Request * Allows to create timestamp requests or parse existing ones. A Request is @@ -1399,6 +1432,7 @@ Init_ossl_ts(void) rb_define_method(cTimestampRequest, "cert_requested=", ossl_ts_req_set_cert_requested, 1); rb_define_method(cTimestampRequest, "cert_requested?", ossl_ts_req_get_cert_requested, 0); rb_define_method(cTimestampRequest, "to_der", ossl_ts_req_to_der, 0); + rb_define_method(cTimestampRequest, "to_text", ossl_ts_req_to_text, 0); /* * Indicates a successful response. Equal to +0+. @@ -1473,67 +1507,45 @@ Init_ossl_ts(void) * fac.default_policy_id = '1.2.3.4.5' * fac.additional_certificates = [ inter1, inter2 ] * timestamp = fac.create_timestamp(p12.key, p12.certificate, req) - * - * ==Attributes - * - * ===default_policy_id + */ + cTimestampFactory = rb_define_class_under(mTimestamp, "Factory", rb_cObject); + /* + * The list of digest algorithms that the factory is allowed + * create timestamps for. Known vulnerable or weak algorithms should not be + * allowed where possible. Must be an Array of String or OpenSSL::Digest + * subclass instances. + */ + rb_attr(cTimestampFactory, rb_intern_const("allowed_digests"), 1, 1, 0); + /* + * A String representing the default policy object identifier, or +nil+. * * Request#policy_id will always be preferred over this if present in the - * Request, only if Request#policy_id is nil default_policy will be used. + * Request, only if Request#policy_id is +nil+ default_policy will be used. * If none of both is present, a TimestampError will be raised when trying * to create a Response. - * - * call-seq: - * factory.default_policy_id = "string" -> string - * factory.default_policy_id -> string or nil - * - * ===serial_number - * - * Sets or retrieves the serial number to be used for timestamp creation. - * Must be present for timestamp creation. - * - * call-seq: - * factory.serial_number = number -> number - * factory.serial_number -> number or nil - * - * ===gen_time - * - * Sets or retrieves the Time value to be used in the Response. Must be - * present for timestamp creation. - * - * call-seq: - * factory.gen_time = Time -> Time - * factory.gen_time -> Time or nil - * - * ===additional_certs - * - * Sets or retrieves additional certificates apart from the timestamp - * certificate (e.g. intermediate certificates) to be added to the Response. - * Must be an Array of OpenSSL::X509::Certificate. - * - * call-seq: - * factory.additional_certs = [cert1, cert2] -> [ cert1, cert2 ] - * factory.additional_certs -> array or nil - * - * ===allowed_digests - * - * Sets or retrieves the digest algorithms that the factory is allowed - * create timestamps for. Known vulnerable or weak algorithms should not be - * allowed where possible. - * Must be an Array of String or OpenSSL::Digest subclass instances. - * - * call-seq: - * factory.allowed_digests = ["sha1", OpenSSL::Digest.new('SHA256').new] -> [ "sha1", OpenSSL::Digest) ] - * factory.allowed_digests -> array or nil - * */ - cTimestampFactory = rb_define_class_under(mTimestamp, "Factory", rb_cObject); - rb_attr(cTimestampFactory, rb_intern_const("allowed_digests"), 1, 1, 0); rb_attr(cTimestampFactory, rb_intern_const("default_policy_id"), 1, 1, 0); + /* + * The serial number to be used for timestamp creation. Must be present for + * timestamp creation. Must be an instance of OpenSSL::BN or Integer. + */ rb_attr(cTimestampFactory, rb_intern_const("serial_number"), 1, 1, 0); + /* + * The Time value to be used in the Response. Must be present for timestamp + * creation. + */ rb_attr(cTimestampFactory, rb_intern_const("gen_time"), 1, 1, 0); + /* + * Additional certificates apart from the timestamp certificate (e.g. + * intermediate certificates) to be added to the Response. + * Must be an Array of OpenSSL::X509::Certificate, or +nil+. + */ rb_attr(cTimestampFactory, rb_intern_const("additional_certs"), 1, 1, 0); rb_define_method(cTimestampFactory, "create_timestamp", ossl_tsfac_create_ts, 3); } - +#else /* OPENSSL_NO_TS */ +void +Init_ossl_ts(void) +{ +} #endif diff --git a/ext/openssl/ossl_ts.h b/ext/openssl/ossl_ts.h index 25fb0e1d64..eeca3046eb 100644 --- a/ext/openssl/ossl_ts.h +++ b/ext/openssl/ossl_ts.h @@ -5,7 +5,7 @@ */ /* * This program is licenced under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(_OSSL_TS_H_) diff --git a/ext/openssl/ossl_x509.c b/ext/openssl/ossl_x509.c index f8470703fc..bc3914fda2 100644 --- a/ext/openssl/ossl_x509.c +++ b/ext/openssl/ossl_x509.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -13,7 +13,8 @@ VALUE mX509; #define DefX509Const(x) rb_define_const(mX509, #x, INT2NUM(X509_##x)) #define DefX509Default(x,i) \ - rb_define_const(mX509, "DEFAULT_" #x, rb_str_new2(X509_get_default_##i())) + rb_define_const(mX509, "DEFAULT_" #x, \ + rb_obj_freeze(rb_str_new_cstr(X509_get_default_##i()))) ASN1_TIME * ossl_x509_time_adjust(ASN1_TIME *s, VALUE time) @@ -29,10 +30,6 @@ ossl_x509_time_adjust(ASN1_TIME *s, VALUE time) void Init_ossl_x509(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); -#endif - mX509 = rb_define_module_under(mOSSL, "X509"); Init_ossl_x509attr(); @@ -48,9 +45,7 @@ Init_ossl_x509(void) /* Certificate verification error code */ DefX509Const(V_OK); -#if defined(X509_V_ERR_UNSPECIFIED) /* 1.0.1r, 1.0.2f, 1.1.0 */ DefX509Const(V_ERR_UNSPECIFIED); -#endif DefX509Const(V_ERR_UNABLE_TO_GET_ISSUER_CERT); DefX509Const(V_ERR_UNABLE_TO_GET_CRL); DefX509Const(V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE); @@ -104,10 +99,10 @@ Init_ossl_x509(void) DefX509Const(V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX); DefX509Const(V_ERR_UNSUPPORTED_NAME_SYNTAX); DefX509Const(V_ERR_CRL_PATH_VALIDATION_ERROR); -#if defined(X509_V_ERR_PATH_LOOP) +#if defined(X509_V_ERR_PATH_LOOP) /* OpenSSL 1.1.0, missing in LibreSSL */ DefX509Const(V_ERR_PATH_LOOP); #endif -#if defined(X509_V_ERR_SUITE_B_INVALID_VERSION) +#if defined(X509_V_ERR_SUITE_B_INVALID_VERSION) /* OpenSSL 1.1.0, missing in LibreSSL */ DefX509Const(V_ERR_SUITE_B_INVALID_VERSION); DefX509Const(V_ERR_SUITE_B_INVALID_ALGORITHM); DefX509Const(V_ERR_SUITE_B_INVALID_CURVE); @@ -118,27 +113,21 @@ Init_ossl_x509(void) DefX509Const(V_ERR_HOSTNAME_MISMATCH); DefX509Const(V_ERR_EMAIL_MISMATCH); DefX509Const(V_ERR_IP_ADDRESS_MISMATCH); -#if defined(X509_V_ERR_DANE_NO_MATCH) +#if defined(X509_V_ERR_DANE_NO_MATCH) /* OpenSSL 1.1.0, missing in LibreSSL */ DefX509Const(V_ERR_DANE_NO_MATCH); #endif -#if defined(X509_V_ERR_EE_KEY_TOO_SMALL) DefX509Const(V_ERR_EE_KEY_TOO_SMALL); DefX509Const(V_ERR_CA_KEY_TOO_SMALL); DefX509Const(V_ERR_CA_MD_TOO_WEAK); -#endif -#if defined(X509_V_ERR_INVALID_CALL) DefX509Const(V_ERR_INVALID_CALL); -#endif -#if defined(X509_V_ERR_STORE_LOOKUP) DefX509Const(V_ERR_STORE_LOOKUP); -#endif -#if defined(X509_V_ERR_NO_VALID_SCTS) +#if defined(X509_V_ERR_NO_VALID_SCTS) /* OpenSSL 1.1.0, missing in LibreSSL */ DefX509Const(V_ERR_NO_VALID_SCTS); #endif -#if defined(X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION) +#if defined(X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION) /* OpenSSL 1.1.0, missing in LibreSSL */ DefX509Const(V_ERR_PROXY_SUBJECT_NAME_VIOLATION); #endif -#if defined(X509_V_ERR_OCSP_VERIFY_NEEDED) +#if defined(X509_V_ERR_OCSP_VERIFY_NEEDED) /* OpenSSL 1.1.1, missing in LibreSSL */ DefX509Const(V_ERR_OCSP_VERIFY_NEEDED); DefX509Const(V_ERR_OCSP_VERIFY_FAILED); DefX509Const(V_ERR_OCSP_CERT_UNKNOWN); @@ -189,17 +178,13 @@ Init_ossl_x509(void) * certificate chain, search the Store first for the issuer certificate. * Enabled by default in OpenSSL >= 1.1.0. */ DefX509Const(V_FLAG_TRUSTED_FIRST); -#if defined(X509_V_FLAG_SUITEB_128_LOS_ONLY) +#if defined(X509_V_FLAG_SUITEB_128_LOS_ONLY) /* OpenSSL 1.1.0, missing in LibreSSL */ /* Set by Store#flags= and StoreContext#flags=. * Enables Suite B 128 bit only mode. */ DefX509Const(V_FLAG_SUITEB_128_LOS_ONLY); -#endif -#if defined(X509_V_FLAG_SUITEB_192_LOS) /* Set by Store#flags= and StoreContext#flags=. * Enables Suite B 192 bit only mode. */ DefX509Const(V_FLAG_SUITEB_192_LOS); -#endif -#if defined(X509_V_FLAG_SUITEB_128_LOS) /* Set by Store#flags= and StoreContext#flags=. * Enables Suite B 128 bit mode allowing 192 bit algorithms. */ DefX509Const(V_FLAG_SUITEB_128_LOS); @@ -207,17 +192,13 @@ Init_ossl_x509(void) /* Set by Store#flags= and StoreContext#flags=. * Allows partial chains if at least one certificate is in trusted store. */ DefX509Const(V_FLAG_PARTIAL_CHAIN); -#if defined(X509_V_FLAG_NO_ALT_CHAINS) /* Set by Store#flags= and StoreContext#flags=. Suppresses searching for * a alternative chain. No effect in OpenSSL >= 1.1.0. */ DefX509Const(V_FLAG_NO_ALT_CHAINS); -#endif -#if defined(X509_V_FLAG_NO_CHECK_TIME) /* Set by Store#flags= and StoreContext#flags=. Suppresses checking the * validity period of certificates and CRLs. No effect when the current * time is explicitly set by Store#time= or StoreContext#time=. */ DefX509Const(V_FLAG_NO_CHECK_TIME); -#endif /* Set by Store#purpose=. SSL/TLS client. */ DefX509Const(PURPOSE_SSL_CLIENT); diff --git a/ext/openssl/ossl_x509.h b/ext/openssl/ossl_x509.h index 4fadfa6b82..d25167ee7b 100644 --- a/ext/openssl/ossl_x509.h +++ b/ext/openssl/ossl_x509.h @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #if !defined(_OSSL_X509_H_) #define _OSSL_X509_H_ @@ -28,7 +28,6 @@ void Init_ossl_x509(void); * X509Attr */ extern VALUE cX509Attr; -extern VALUE eX509AttrError; VALUE ossl_x509attr_new(X509_ATTRIBUTE *); X509_ATTRIBUTE *GetX509AttrPtr(VALUE); @@ -38,7 +37,6 @@ void Init_ossl_x509attr(void); * X509Cert */ extern VALUE cX509Cert; -extern VALUE eX509CertError; VALUE ossl_x509_new(X509 *); X509 *GetX509CertPtr(VALUE); @@ -48,9 +46,6 @@ void Init_ossl_x509cert(void); /* * X509CRL */ -extern VALUE cX509CRL; -extern VALUE eX509CRLError; - VALUE ossl_x509crl_new(X509_CRL *); X509_CRL *GetX509CRLPtr(VALUE); void Init_ossl_x509crl(void); @@ -59,8 +54,6 @@ void Init_ossl_x509crl(void); * X509Extension */ extern VALUE cX509Ext; -extern VALUE cX509ExtFactory; -extern VALUE eX509ExtError; VALUE ossl_x509ext_new(X509_EXTENSION *); X509_EXTENSION *GetX509ExtPtr(VALUE); @@ -69,9 +62,6 @@ void Init_ossl_x509ext(void); /* * X509Name */ -extern VALUE cX509Name; -extern VALUE eX509NameError; - VALUE ossl_x509name_new(X509_NAME *); X509_NAME *GetX509NamePtr(VALUE); void Init_ossl_x509name(void); @@ -79,9 +69,6 @@ void Init_ossl_x509name(void); /* * X509Request */ -extern VALUE cX509Req; -extern VALUE eX509ReqError; - X509_REQ *GetX509ReqPtr(VALUE); void Init_ossl_x509req(void); @@ -89,7 +76,6 @@ void Init_ossl_x509req(void); * X509Revoked */ extern VALUE cX509Rev; -extern VALUE eX509RevError; VALUE ossl_x509revoked_new(X509_REVOKED *); X509_REVOKED *DupX509RevokedPtr(VALUE); @@ -98,12 +84,7 @@ void Init_ossl_x509revoked(void); /* * X509Store and X509StoreContext */ -extern VALUE cX509Store; -extern VALUE cX509StoreContext; -extern VALUE eX509StoreError; - X509_STORE *GetX509StorePtr(VALUE); - void Init_ossl_x509store(void); /* diff --git a/ext/openssl/ossl_x509attr.c b/ext/openssl/ossl_x509attr.c index d1d8bb5e95..4769e56e1e 100644 --- a/ext/openssl/ossl_x509attr.c +++ b/ext/openssl/ossl_x509attr.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -13,14 +13,14 @@ TypedData_Wrap_Struct((klass), &ossl_x509attr_type, 0) #define SetX509Attr(obj, attr) do { \ if (!(attr)) { \ - ossl_raise(rb_eRuntimeError, "ATTR wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "ATTR wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (attr); \ } while (0) #define GetX509Attr(obj, attr) do { \ TypedData_Get_Struct((obj), X509_ATTRIBUTE, &ossl_x509attr_type, (attr)); \ if (!(attr)) { \ - ossl_raise(rb_eRuntimeError, "ATTR wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "ATTR wasn't initialized!"); \ } \ } while (0) @@ -28,7 +28,7 @@ * Classes */ VALUE cX509Attr; -VALUE eX509AttrError; +static VALUE eX509AttrError; static void ossl_x509attr_free(void *ptr) @@ -39,7 +39,7 @@ ossl_x509attr_free(void *ptr) static const rb_data_type_t ossl_x509attr_type = { "OpenSSL/X509/ATTRIBUTE", { - 0, ossl_x509attr_free, + 0, ossl_x509attr_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -54,14 +54,9 @@ ossl_x509attr_new(X509_ATTRIBUTE *attr) VALUE obj; obj = NewX509Attr(cX509Attr); - if (!attr) { - new = X509_ATTRIBUTE_new(); - } else { - new = X509_ATTRIBUTE_dup(attr); - } - if (!new) { - ossl_raise(eX509AttrError, NULL); - } + new = X509_ATTRIBUTE_dup(attr); + if (!new) + ossl_raise(eX509AttrError, "X509_ATTRIBUTE_dup"); SetX509Attr(obj, new); return obj; @@ -88,7 +83,7 @@ ossl_x509attr_alloc(VALUE klass) obj = NewX509Attr(klass); if (!(attr = X509_ATTRIBUTE_new())) - ossl_raise(eX509AttrError, NULL); + ossl_raise(eX509AttrError, NULL); SetX509Attr(obj, attr); return obj; @@ -107,15 +102,15 @@ ossl_x509attr_initialize(int argc, VALUE *argv, VALUE self) GetX509Attr(self, attr); if(rb_scan_args(argc, argv, "11", &oid, &value) == 1){ - oid = ossl_to_der_if_possible(oid); - StringValue(oid); - p = (unsigned char *)RSTRING_PTR(oid); - x = d2i_X509_ATTRIBUTE(&attr, &p, RSTRING_LEN(oid)); - DATA_PTR(self) = attr; - if(!x){ - ossl_raise(eX509AttrError, NULL); - } - return self; + oid = ossl_to_der_if_possible(oid); + StringValue(oid); + p = (unsigned char *)RSTRING_PTR(oid); + x = d2i_X509_ATTRIBUTE(&attr, &p, RSTRING_LEN(oid)); + DATA_PTR(self) = attr; + if(!x){ + ossl_raise(eX509AttrError, NULL); + } + return self; } rb_funcall(self, rb_intern("oid="), 1, oid); rb_funcall(self, rb_intern("value="), 1, value); @@ -123,6 +118,7 @@ ossl_x509attr_initialize(int argc, VALUE *argv, VALUE self) return self; } +/* :nodoc: */ static VALUE ossl_x509attr_initialize_copy(VALUE self, VALUE other) { @@ -134,7 +130,7 @@ ossl_x509attr_initialize_copy(VALUE self, VALUE other) attr_new = X509_ATTRIBUTE_dup(attr_other); if (!attr_new) - ossl_raise(eX509AttrError, "X509_ATTRIBUTE_dup"); + ossl_raise(eX509AttrError, "X509_ATTRIBUTE_dup"); SetX509Attr(self, attr_new); X509_ATTRIBUTE_free(attr); @@ -158,8 +154,8 @@ ossl_x509attr_set_oid(VALUE self, VALUE oid) obj = OBJ_txt2obj(s, 0); if(!obj) ossl_raise(eX509AttrError, NULL); if (!X509_ATTRIBUTE_set1_object(attr, obj)) { - ASN1_OBJECT_free(obj); - ossl_raise(eX509AttrError, "X509_ATTRIBUTE_set1_object"); + ASN1_OBJECT_free(obj); + ossl_raise(eX509AttrError, "X509_ATTRIBUTE_set1_object"); } ASN1_OBJECT_free(obj); @@ -168,29 +164,18 @@ ossl_x509attr_set_oid(VALUE self, VALUE oid) /* * call-seq: - * attr.oid => string + * attr.oid -> string + * + * Returns the OID of the attribute. Returns the short name or the dotted + * decimal notation. */ static VALUE ossl_x509attr_get_oid(VALUE self) { X509_ATTRIBUTE *attr; - ASN1_OBJECT *oid; - BIO *out; - VALUE ret; - int nid; GetX509Attr(self, attr); - oid = X509_ATTRIBUTE_get0_object(attr); - if ((nid = OBJ_obj2nid(oid)) != NID_undef) - ret = rb_str_new2(OBJ_nid2sn(nid)); - else{ - if (!(out = BIO_new(BIO_s_mem()))) - ossl_raise(eX509AttrError, NULL); - i2a_ASN1_OBJECT(out, oid); - ret = ossl_membio2str(out); - } - - return ret; + return ossl_asn1obj_to_string(X509_ATTRIBUTE_get0_object(attr)); } /* @@ -201,37 +186,36 @@ static VALUE ossl_x509attr_set_value(VALUE self, VALUE value) { X509_ATTRIBUTE *attr; - VALUE asn1_value; - int i, asn1_tag; + GetX509Attr(self, attr); OSSL_Check_Kind(value, cASN1Data); - asn1_tag = NUM2INT(rb_attr_get(value, rb_intern("@tag"))); - asn1_value = rb_attr_get(value, rb_intern("@value")); - if (asn1_tag != V_ASN1_SET) - ossl_raise(eASN1Error, "argument must be ASN1::Set"); - if (!RB_TYPE_P(asn1_value, T_ARRAY)) - ossl_raise(eASN1Error, "ASN1::Set has non-array value"); + VALUE der = ossl_to_der(value); + const unsigned char *p = (const unsigned char *)RSTRING_PTR(der); + STACK_OF(ASN1_TYPE) *sk = d2i_ASN1_SET_ANY(NULL, &p, RSTRING_LEN(der)); + if (!sk) + ossl_raise(eX509AttrError, "attribute value must be ASN1::Set"); - GetX509Attr(self, attr); if (X509_ATTRIBUTE_count(attr)) { /* populated, reset first */ - ASN1_OBJECT *obj = X509_ATTRIBUTE_get0_object(attr); - X509_ATTRIBUTE *new_attr = X509_ATTRIBUTE_create_by_OBJ(NULL, obj, 0, NULL, -1); - if (!new_attr) - ossl_raise(eX509AttrError, NULL); - SetX509Attr(self, new_attr); - X509_ATTRIBUTE_free(attr); - attr = new_attr; + ASN1_OBJECT *obj = X509_ATTRIBUTE_get0_object(attr); + X509_ATTRIBUTE *new_attr = X509_ATTRIBUTE_create_by_OBJ(NULL, obj, 0, NULL, -1); + if (!new_attr) { + sk_ASN1_TYPE_pop_free(sk, ASN1_TYPE_free); + ossl_raise(eX509AttrError, "X509_ATTRIBUTE_create_by_OBJ"); + } + SetX509Attr(self, new_attr); + X509_ATTRIBUTE_free(attr); + attr = new_attr; } - for (i = 0; i < RARRAY_LEN(asn1_value); i++) { - ASN1_TYPE *a1type = ossl_asn1_get_asn1type(RARRAY_AREF(asn1_value, i)); - if (!X509_ATTRIBUTE_set1_data(attr, ASN1_TYPE_get(a1type), - a1type->value.ptr, -1)) { - ASN1_TYPE_free(a1type); - ossl_raise(eX509AttrError, NULL); - } - ASN1_TYPE_free(a1type); + for (int i = 0; i < sk_ASN1_TYPE_num(sk); i++) { + ASN1_TYPE *a1type = sk_ASN1_TYPE_value(sk, i); + if (!X509_ATTRIBUTE_set1_data(attr, ASN1_TYPE_get(a1type), + a1type->value.ptr, -1)) { + sk_ASN1_TYPE_pop_free(sk, ASN1_TYPE_free); + ossl_raise(eX509AttrError, "X509_ATTRIBUTE_set1_data"); + } } + sk_ASN1_TYPE_pop_free(sk, ASN1_TYPE_free); return value; } @@ -252,21 +236,21 @@ ossl_x509attr_get_value(VALUE self) GetX509Attr(self, attr); /* there is no X509_ATTRIBUTE_get0_set() :( */ if (!(sk = sk_ASN1_TYPE_new_null())) - ossl_raise(eX509AttrError, "sk_new"); + ossl_raise(eX509AttrError, "sk_new"); count = X509_ATTRIBUTE_count(attr); for (i = 0; i < count; i++) - sk_ASN1_TYPE_push(sk, X509_ATTRIBUTE_get0_type(attr, i)); + sk_ASN1_TYPE_push(sk, X509_ATTRIBUTE_get0_type(attr, i)); if ((len = i2d_ASN1_SET_ANY(sk, NULL)) <= 0) { - sk_ASN1_TYPE_free(sk); - ossl_raise(eX509AttrError, NULL); + sk_ASN1_TYPE_free(sk); + ossl_raise(eX509AttrError, NULL); } str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_ASN1_SET_ANY(sk, &p) <= 0) { - sk_ASN1_TYPE_free(sk); - ossl_raise(eX509AttrError, NULL); + sk_ASN1_TYPE_free(sk); + ossl_raise(eX509AttrError, NULL); } ossl_str_adjust(str, p); sk_ASN1_TYPE_free(sk); @@ -288,11 +272,11 @@ ossl_x509attr_to_der(VALUE self) GetX509Attr(self, attr); if((len = i2d_X509_ATTRIBUTE(attr, NULL)) <= 0) - ossl_raise(eX509AttrError, NULL); + ossl_raise(eX509AttrError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_X509_ATTRIBUTE(attr, &p) <= 0) - ossl_raise(eX509AttrError, NULL); + ossl_raise(eX509AttrError, NULL); ossl_str_adjust(str, p); return str; @@ -304,12 +288,6 @@ ossl_x509attr_to_der(VALUE self) void Init_ossl_x509attr(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - mX509 = rb_define_module_under(mOSSL, "X509"); -#endif - eX509AttrError = rb_define_class_under(mX509, "AttributeError", eOSSLError); cX509Attr = rb_define_class_under(mX509, "Attribute", rb_cObject); diff --git a/ext/openssl/ossl_x509cert.c b/ext/openssl/ossl_x509cert.c index aa6b9bb7ce..95679c7d24 100644 --- a/ext/openssl/ossl_x509cert.c +++ b/ext/openssl/ossl_x509cert.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -13,14 +13,14 @@ TypedData_Wrap_Struct((klass), &ossl_x509_type, 0) #define SetX509(obj, x509) do { \ if (!(x509)) { \ - ossl_raise(rb_eRuntimeError, "CERT wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "CERT wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (x509); \ } while (0) #define GetX509(obj, x509) do { \ TypedData_Get_Struct((obj), X509, &ossl_x509_type, (x509)); \ if (!(x509)) { \ - ossl_raise(rb_eRuntimeError, "CERT wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "CERT wasn't initialized!"); \ } \ } while (0) @@ -28,7 +28,7 @@ * Classes */ VALUE cX509Cert; -VALUE eX509CertError; +static VALUE eX509CertError; static void ossl_x509_free(void *ptr) @@ -39,7 +39,7 @@ ossl_x509_free(void *ptr) static const rb_data_type_t ossl_x509_type = { "OpenSSL/X509", { - 0, ossl_x509_free, + 0, ossl_x509_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -54,14 +54,9 @@ ossl_x509_new(X509 *x509) VALUE obj; obj = NewX509(cX509Cert); - if (!x509) { - new = X509_new(); - } else { - new = X509_dup(x509); - } - if (!new) { - ossl_raise(eX509CertError, NULL); - } + new = X509_dup(x509); + if (!new) + ossl_raise(eX509CertError, "X509_dup"); SetX509(obj, new); return obj; @@ -120,8 +115,8 @@ ossl_x509_initialize(int argc, VALUE *argv, VALUE self) rb_check_frozen(self); if (rb_scan_args(argc, argv, "01", &arg) == 0) { - /* create just empty X509Cert */ - return self; + /* create just empty X509Cert */ + return self; } arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); @@ -140,6 +135,7 @@ ossl_x509_initialize(int argc, VALUE *argv, VALUE self) return self; } +/* :nodoc: */ static VALUE ossl_x509_copy(VALUE self, VALUE other) { @@ -174,11 +170,11 @@ ossl_x509_to_der(VALUE self) GetX509(self, x509); if ((len = i2d_X509(x509, NULL)) <= 0) - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_X509(x509, &p) <= 0) - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); ossl_str_adjust(str, p); return str; @@ -200,8 +196,8 @@ ossl_x509_to_pem(VALUE self) if (!out) ossl_raise(eX509CertError, NULL); if (!PEM_write_bio_X509(out, x509)) { - BIO_free(out); - ossl_raise(eX509CertError, NULL); + BIO_free(out); + ossl_raise(eX509CertError, NULL); } str = ossl_membio2str(out); @@ -225,8 +221,8 @@ ossl_x509_to_text(VALUE self) if (!out) ossl_raise(eX509CertError, NULL); if (!X509_print(out, x509)) { - BIO_free(out); - ossl_raise(eX509CertError, NULL); + BIO_free(out); + ossl_raise(eX509CertError, NULL); } str = ossl_membio2str(out); @@ -246,7 +242,7 @@ ossl_x509_to_req(VALUE self) GetX509(self, x509); if (!(req = X509_to_X509_REQ(x509, NULL, EVP_md5()))) { - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } obj = ossl_x509req_new(req); X509_REQ_free(req); @@ -280,11 +276,11 @@ ossl_x509_set_version(VALUE self, VALUE version) long ver; if ((ver = NUM2LONG(version)) < 0) { - ossl_raise(eX509CertError, "version must be >= 0!"); + ossl_raise(eX509CertError, "version must be >= 0!"); } GetX509(self, x509); if (!X509_set_version(x509, ver)) { - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } return version; @@ -322,25 +318,23 @@ ossl_x509_set_serial(VALUE self, VALUE num) /* * call-seq: * cert.signature_algorithm => string + * + * Returns the signature algorithm used to sign this certificate. This returns + * the algorithm name found in the TBSCertificate structure, not the outer + * \Certificate structure. + * + * Returns the long name of the signature algorithm, or the dotted decimal + * notation if \OpenSSL does not define a long name for it. */ static VALUE ossl_x509_get_signature_algorithm(VALUE self) { X509 *x509; - BIO *out; - VALUE str; + const ASN1_OBJECT *obj; GetX509(self, x509); - out = BIO_new(BIO_s_mem()); - if (!out) ossl_raise(eX509CertError, NULL); - - if (!i2a_ASN1_OBJECT(out, X509_get0_tbs_sigalg(x509)->algorithm)) { - BIO_free(out); - ossl_raise(eX509CertError, NULL); - } - str = ossl_membio2str(out); - - return str; + X509_ALGOR_get0(&obj, NULL, NULL, X509_get0_tbs_sigalg(x509)); + return ossl_asn1obj_to_string_long_name(obj); } /* @@ -355,7 +349,7 @@ ossl_x509_get_subject(VALUE self) GetX509(self, x509); if (!(name = X509_get_subject_name(x509))) { /* NO DUP - don't free! */ - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } return ossl_x509name_new(name); @@ -372,7 +366,7 @@ ossl_x509_set_subject(VALUE self, VALUE subject) GetX509(self, x509); if (!X509_set_subject_name(x509, GetX509NamePtr(subject))) { /* DUPs name */ - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } return subject; @@ -390,7 +384,7 @@ ossl_x509_get_issuer(VALUE self) GetX509(self, x509); if(!(name = X509_get_issuer_name(x509))) { /* NO DUP - don't free! */ - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } return ossl_x509name_new(name); @@ -407,7 +401,7 @@ ossl_x509_set_issuer(VALUE self, VALUE issuer) GetX509(self, x509); if (!X509_set_issuer_name(x509, GetX509NamePtr(issuer))) { /* DUPs name */ - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } return issuer; @@ -425,7 +419,7 @@ ossl_x509_get_not_before(VALUE self) GetX509(self, x509); if (!(asn1time = X509_get0_notBefore(x509))) { - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } return asn1time_to_time(asn1time); @@ -444,8 +438,8 @@ ossl_x509_set_not_before(VALUE self, VALUE time) GetX509(self, x509); asn1time = ossl_x509_time_adjust(NULL, time); if (!X509_set1_notBefore(x509, asn1time)) { - ASN1_TIME_free(asn1time); - ossl_raise(eX509CertError, "X509_set_notBefore"); + ASN1_TIME_free(asn1time); + ossl_raise(eX509CertError, "X509_set_notBefore"); } ASN1_TIME_free(asn1time); @@ -464,7 +458,7 @@ ossl_x509_get_not_after(VALUE self) GetX509(self, x509); if (!(asn1time = X509_get0_notAfter(x509))) { - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } return asn1time_to_time(asn1time); @@ -483,8 +477,8 @@ ossl_x509_set_not_after(VALUE self, VALUE time) GetX509(self, x509); asn1time = ossl_x509_time_adjust(NULL, time); if (!X509_set1_notAfter(x509, asn1time)) { - ASN1_TIME_free(asn1time); - ossl_raise(eX509CertError, "X509_set_notAfter"); + ASN1_TIME_free(asn1time); + ossl_raise(eX509CertError, "X509_set_notAfter"); } ASN1_TIME_free(asn1time); @@ -503,10 +497,10 @@ ossl_x509_get_public_key(VALUE self) GetX509(self, x509); if (!(pkey = X509_get_pubkey(x509))) { /* adds an reference */ - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } - return ossl_pkey_new(pkey); /* NO DUP - OK */ + return ossl_pkey_wrap(pkey); } /* @@ -523,7 +517,7 @@ ossl_x509_set_public_key(VALUE self, VALUE key) pkey = GetPKeyPtr(key); ossl_pkey_check_public_key(pkey); if (!X509_set_pubkey(x509, pkey)) - ossl_raise(eX509CertError, "X509_set_pubkey"); + ossl_raise(eX509CertError, "X509_set_pubkey"); return key; } @@ -537,13 +531,14 @@ ossl_x509_sign(VALUE self, VALUE key, VALUE digest) X509 *x509; EVP_PKEY *pkey; const EVP_MD *md; + VALUE md_holder; pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ - md = ossl_evp_get_digestbyname(digest); + /* NULL needed for some key types, e.g. Ed25519 */ + md = NIL_P(digest) ? NULL : ossl_evp_md_fetch(digest, &md_holder); GetX509(self, x509); - if (!X509_sign(x509, pkey, md)) { - ossl_raise(eX509CertError, NULL); - } + if (!X509_sign(x509, pkey, md)) + ossl_raise(eX509CertError, "X509_sign"); return self; } @@ -566,12 +561,12 @@ ossl_x509_verify(VALUE self, VALUE key) ossl_pkey_check_public_key(pkey); switch (X509_verify(x509, pkey)) { case 1: - return Qtrue; + return Qtrue; case 0: - ossl_clear_error(); - return Qfalse; + ossl_clear_error(); + return Qfalse; default: - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } } @@ -592,8 +587,8 @@ ossl_x509_check_private_key(VALUE self, VALUE key) pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ GetX509(self, x509); if (!X509_check_private_key(x509, pkey)) { - ossl_clear_error(); - return Qfalse; + ossl_clear_error(); + return Qfalse; } return Qtrue; @@ -613,13 +608,10 @@ ossl_x509_get_extensions(VALUE self) GetX509(self, x509); count = X509_get_ext_count(x509); - if (count < 0) { - return rb_ary_new(); - } - ary = rb_ary_new2(count); + ary = rb_ary_new_capa(count); for (i=0; i<count; i++) { - ext = X509_get_ext(x509, i); /* NO DUP - don't free! */ - rb_ary_push(ary, ossl_x509ext_new(ext)); + ext = X509_get_ext(x509, i); /* NO DUP - don't free! */ + rb_ary_push(ary, ossl_x509ext_new(ext)); } return ary; @@ -639,16 +631,16 @@ ossl_x509_set_extensions(VALUE self, VALUE ary) Check_Type(ary, T_ARRAY); /* All ary's members should be X509Extension */ for (i=0; i<RARRAY_LEN(ary); i++) { - OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext); + OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext); } GetX509(self, x509); for (i = X509_get_ext_count(x509); i > 0; i--) X509_EXTENSION_free(X509_delete_ext(x509, 0)); for (i=0; i<RARRAY_LEN(ary); i++) { - ext = GetX509ExtPtr(RARRAY_AREF(ary, i)); - if (!X509_add_ext(x509, ext, -1)) { /* DUPs ext */ - ossl_raise(eX509CertError, "X509_add_ext"); - } + ext = GetX509ExtPtr(RARRAY_AREF(ary, i)); + if (!X509_add_ext(x509, ext, -1)) { /* DUPs ext */ + ossl_raise(eX509CertError, "X509_add_ext"); + } } return ary; @@ -667,32 +659,24 @@ ossl_x509_add_extension(VALUE self, VALUE extension) GetX509(self, x509); ext = GetX509ExtPtr(extension); if (!X509_add_ext(x509, ext, -1)) { /* DUPs ext - FREE it */ - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } return extension; } -static VALUE -ossl_x509_inspect(VALUE self) -{ - return rb_sprintf("#<%"PRIsVALUE": subject=%+"PRIsVALUE", " - "issuer=%+"PRIsVALUE", serial=%+"PRIsVALUE", " - "not_before=%+"PRIsVALUE", not_after=%+"PRIsVALUE">", - rb_obj_class(self), - ossl_x509_get_subject(self), - ossl_x509_get_issuer(self), - ossl_x509_get_serial(self), - ossl_x509_get_not_before(self), - ossl_x509_get_not_after(self)); -} - /* * call-seq: * cert1 == cert2 -> true | false * * Compares the two certificates. Note that this takes into account all fields, * not just the issuer name and the serial number. + * + * This method uses X509_cmp() from OpenSSL, which compares certificates based + * on their cached DER encodings. The comparison can be unreliable if a + * certificate is incomplete. + * + * See also the man page X509_cmp(3). */ static VALUE ossl_x509_eq(VALUE self, VALUE other) @@ -701,12 +685,42 @@ ossl_x509_eq(VALUE self, VALUE other) GetX509(self, a); if (!rb_obj_is_kind_of(other, cX509Cert)) - return Qfalse; + return Qfalse; GetX509(other, b); return !X509_cmp(a, b) ? Qtrue : Qfalse; } +/* + * call-seq: + * cert.tbs_bytes => string + * + * Returns the DER-encoded bytes of the certificate's to be signed certificate. + * This is mainly useful for validating embedded certificate transparency signatures. + */ +static VALUE +ossl_x509_tbs_bytes(VALUE self) +{ + X509 *x509; + int len; + unsigned char *p0; + VALUE str; + + GetX509(self, x509); + len = i2d_re_X509_tbs(x509, NULL); + if (len <= 0) { + ossl_raise(eX509CertError, "i2d_re_X509_tbs"); + } + str = rb_str_new(NULL, len); + p0 = (unsigned char *)RSTRING_PTR(str); + if (i2d_re_X509_tbs(x509, &p0) <= 0) { + ossl_raise(eX509CertError, "i2d_re_X509_tbs"); + } + ossl_str_adjust(str, p0); + + return str; +} + struct load_chained_certificates_arguments { VALUE certificates; X509 *certificate; @@ -766,7 +780,7 @@ load_chained_certificates_PEM(BIO *in) { certificates = load_chained_certificates_append(Qnil, certificate); while ((certificate = PEM_read_bio_X509(in, NULL, NULL, NULL))) { - load_chained_certificates_append(certificates, certificate); + load_chained_certificates_append(certificates, certificate); } /* We tried to read one more certificate but could not read start line: */ @@ -864,12 +878,6 @@ ossl_x509_load(VALUE klass, VALUE buffer) void Init_ossl_x509cert(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - mX509 = rb_define_module_under(mOSSL, "X509"); -#endif - eX509CertError = rb_define_class_under(mX509, "CertificateError", eOSSLError); /* Document-class: OpenSSL::X509::Certificate @@ -997,6 +1005,6 @@ Init_ossl_x509cert(void) rb_define_method(cX509Cert, "extensions", ossl_x509_get_extensions, 0); rb_define_method(cX509Cert, "extensions=", ossl_x509_set_extensions, 1); rb_define_method(cX509Cert, "add_extension", ossl_x509_add_extension, 1); - rb_define_method(cX509Cert, "inspect", ossl_x509_inspect, 0); rb_define_method(cX509Cert, "==", ossl_x509_eq, 1); + rb_define_method(cX509Cert, "tbs_bytes", ossl_x509_tbs_bytes, 0); } diff --git a/ext/openssl/ossl_x509crl.c b/ext/openssl/ossl_x509crl.c index 80e29f9df2..a221429c34 100644 --- a/ext/openssl/ossl_x509crl.c +++ b/ext/openssl/ossl_x509crl.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -13,22 +13,22 @@ TypedData_Wrap_Struct((klass), &ossl_x509crl_type, 0) #define SetX509CRL(obj, crl) do { \ if (!(crl)) { \ - ossl_raise(rb_eRuntimeError, "CRL wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "CRL wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (crl); \ } while (0) #define GetX509CRL(obj, crl) do { \ TypedData_Get_Struct((obj), X509_CRL, &ossl_x509crl_type, (crl)); \ if (!(crl)) { \ - ossl_raise(rb_eRuntimeError, "CRL wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "CRL wasn't initialized!"); \ } \ } while (0) /* * Classes */ -VALUE cX509CRL; -VALUE eX509CRLError; +static VALUE cX509CRL; +static VALUE eX509CRLError; static void ossl_x509crl_free(void *ptr) @@ -39,7 +39,7 @@ ossl_x509crl_free(void *ptr) static const rb_data_type_t ossl_x509crl_type = { "OpenSSL/X509/CRL", { - 0, ossl_x509crl_free, + 0, ossl_x509crl_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -64,8 +64,9 @@ ossl_x509crl_new(X509_CRL *crl) VALUE obj; obj = NewX509CRL(cX509CRL); - tmp = crl ? X509_CRL_dup(crl) : X509_CRL_new(); - if(!tmp) ossl_raise(eX509CRLError, NULL); + tmp = X509_CRL_dup(crl); + if (!tmp) + ossl_raise(eX509CRLError, "X509_CRL_dup"); SetX509CRL(obj, tmp); return obj; @@ -82,7 +83,7 @@ ossl_x509crl_alloc(VALUE klass) obj = NewX509CRL(klass); if (!(crl = X509_CRL_new())) { - ossl_raise(eX509CRLError, NULL); + ossl_raise(eX509CRLError, NULL); } SetX509CRL(obj, crl); @@ -98,7 +99,7 @@ ossl_x509crl_initialize(int argc, VALUE *argv, VALUE self) rb_check_frozen(self); if (rb_scan_args(argc, argv, "01", &arg) == 0) { - return self; + return self; } arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); @@ -117,6 +118,7 @@ ossl_x509crl_initialize(int argc, VALUE *argv, VALUE self) return self; } +/* :nodoc: */ static VALUE ossl_x509crl_copy(VALUE self, VALUE other) { @@ -127,7 +129,7 @@ ossl_x509crl_copy(VALUE self, VALUE other) GetX509CRL(self, a); GetX509CRL(other, b); if (!(crl = X509_CRL_dup(b))) { - ossl_raise(eX509CRLError, NULL); + ossl_raise(eX509CRLError, NULL); } X509_CRL_free(a); DATA_PTR(self) = crl; @@ -154,34 +156,36 @@ ossl_x509crl_set_version(VALUE self, VALUE version) long ver; if ((ver = NUM2LONG(version)) < 0) { - ossl_raise(eX509CRLError, "version must be >= 0!"); + ossl_raise(eX509CRLError, "version must be >= 0!"); } GetX509CRL(self, crl); if (!X509_CRL_set_version(crl, ver)) { - ossl_raise(eX509CRLError, NULL); + ossl_raise(eX509CRLError, NULL); } return version; } +/* + * call-seq: + * crl.signature_algorithm -> string + * + * Returns the signature algorithm used to sign this CRL. + * + * Returns the long name of the signature algorithm, or the dotted decimal + * notation if \OpenSSL does not define a long name for it. + */ static VALUE ossl_x509crl_get_signature_algorithm(VALUE self) { X509_CRL *crl; const X509_ALGOR *alg; - BIO *out; + const ASN1_OBJECT *obj; GetX509CRL(self, crl); - if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eX509CRLError, NULL); - } X509_CRL_get0_signature(crl, NULL, &alg); - if (!i2a_ASN1_OBJECT(out, alg->algorithm)) { - BIO_free(out); - ossl_raise(eX509CRLError, NULL); - } - - return ossl_membio2str(out); + X509_ALGOR_get0(&obj, NULL, NULL, alg); + return ossl_asn1obj_to_string_long_name(obj); } static VALUE @@ -202,7 +206,7 @@ ossl_x509crl_set_issuer(VALUE self, VALUE issuer) GetX509CRL(self, crl); if (!X509_CRL_set_issuer_name(crl, GetX509NamePtr(issuer))) { /* DUPs name */ - ossl_raise(eX509CRLError, NULL); + ossl_raise(eX509CRLError, NULL); } return issuer; } @@ -216,7 +220,7 @@ ossl_x509crl_get_last_update(VALUE self) GetX509CRL(self, crl); time = X509_CRL_get0_lastUpdate(crl); if (!time) - return Qnil; + return Qnil; return asn1time_to_time(time); } @@ -230,8 +234,8 @@ ossl_x509crl_set_last_update(VALUE self, VALUE time) GetX509CRL(self, crl); asn1time = ossl_x509_time_adjust(NULL, time); if (!X509_CRL_set1_lastUpdate(crl, asn1time)) { - ASN1_TIME_free(asn1time); - ossl_raise(eX509CRLError, "X509_CRL_set_lastUpdate"); + ASN1_TIME_free(asn1time); + ossl_raise(eX509CRLError, "X509_CRL_set_lastUpdate"); } ASN1_TIME_free(asn1time); @@ -247,7 +251,7 @@ ossl_x509crl_get_next_update(VALUE self) GetX509CRL(self, crl); time = X509_CRL_get0_nextUpdate(crl); if (!time) - return Qnil; + return Qnil; return asn1time_to_time(time); } @@ -261,8 +265,8 @@ ossl_x509crl_set_next_update(VALUE self, VALUE time) GetX509CRL(self, crl); asn1time = ossl_x509_time_adjust(NULL, time); if (!X509_CRL_set1_nextUpdate(crl, asn1time)) { - ASN1_TIME_free(asn1time); - ossl_raise(eX509CRLError, "X509_CRL_set_nextUpdate"); + ASN1_TIME_free(asn1time); + ossl_raise(eX509CRLError, "X509_CRL_set_nextUpdate"); } ASN1_TIME_free(asn1time); @@ -274,21 +278,19 @@ ossl_x509crl_get_revoked(VALUE self) { X509_CRL *crl; int i, num; - X509_REVOKED *rev; - VALUE ary, revoked; + STACK_OF(X509_REVOKED) *sk; + VALUE ary; GetX509CRL(self, crl); - num = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl)); - if (num < 0) { - OSSL_Debug("num < 0???"); - return rb_ary_new(); - } - ary = rb_ary_new2(num); + sk = X509_CRL_get_REVOKED(crl); + if (!sk) + return rb_ary_new(); + + num = sk_X509_REVOKED_num(sk); + ary = rb_ary_new_capa(num); for(i=0; i<num; i++) { - /* NO DUP - don't free! */ - rev = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); - revoked = ossl_x509revoked_new(rev); - rb_ary_push(ary, revoked); + X509_REVOKED *rev = sk_X509_REVOKED_value(sk, i); + rb_ary_push(ary, ossl_x509revoked_new(rev)); } return ary; @@ -305,19 +307,19 @@ ossl_x509crl_set_revoked(VALUE self, VALUE ary) Check_Type(ary, T_ARRAY); /* All ary members should be X509 Revoked */ for (i=0; i<RARRAY_LEN(ary); i++) { - OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Rev); + OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Rev); } GetX509CRL(self, crl); if ((sk = X509_CRL_get_REVOKED(crl))) { - while ((rev = sk_X509_REVOKED_pop(sk))) - X509_REVOKED_free(rev); + while ((rev = sk_X509_REVOKED_pop(sk))) + X509_REVOKED_free(rev); } for (i=0; i<RARRAY_LEN(ary); i++) { - rev = DupX509RevokedPtr(RARRAY_AREF(ary, i)); - if (!X509_CRL_add0_revoked(crl, rev)) { /* NO DUP - don't free! */ - X509_REVOKED_free(rev); - ossl_raise(eX509CRLError, "X509_CRL_add0_revoked"); - } + rev = DupX509RevokedPtr(RARRAY_AREF(ary, i)); + if (!X509_CRL_add0_revoked(crl, rev)) { /* NO DUP - don't free! */ + X509_REVOKED_free(rev); + ossl_raise(eX509CRLError, "X509_CRL_add0_revoked"); + } } X509_CRL_sort(crl); @@ -333,8 +335,8 @@ ossl_x509crl_add_revoked(VALUE self, VALUE revoked) GetX509CRL(self, crl); rev = DupX509RevokedPtr(revoked); if (!X509_CRL_add0_revoked(crl, rev)) { /* NO DUP - don't free! */ - X509_REVOKED_free(rev); - ossl_raise(eX509CRLError, "X509_CRL_add0_revoked"); + X509_REVOKED_free(rev); + ossl_raise(eX509CRLError, "X509_CRL_add0_revoked"); } X509_CRL_sort(crl); @@ -347,13 +349,14 @@ ossl_x509crl_sign(VALUE self, VALUE key, VALUE digest) X509_CRL *crl; EVP_PKEY *pkey; const EVP_MD *md; + VALUE md_holder; GetX509CRL(self, crl); pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ - md = ossl_evp_get_digestbyname(digest); - if (!X509_CRL_sign(crl, pkey, md)) { - ossl_raise(eX509CRLError, NULL); - } + /* NULL needed for some key types, e.g. Ed25519 */ + md = NIL_P(digest) ? NULL : ossl_evp_md_fetch(digest, &md_holder); + if (!X509_CRL_sign(crl, pkey, md)) + ossl_raise(eX509CRLError, "X509_CRL_sign"); return self; } @@ -369,12 +372,12 @@ ossl_x509crl_verify(VALUE self, VALUE key) ossl_pkey_check_public_key(pkey); switch (X509_CRL_verify(crl, pkey)) { case 1: - return Qtrue; + return Qtrue; case 0: - ossl_clear_error(); - return Qfalse; + ossl_clear_error(); + return Qfalse; default: - ossl_raise(eX509CRLError, NULL); + ossl_raise(eX509CRLError, NULL); } } @@ -386,11 +389,11 @@ ossl_x509crl_to_der(VALUE self) GetX509CRL(self, crl); if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eX509CRLError, NULL); + ossl_raise(eX509CRLError, NULL); } if (!i2d_X509_CRL_bio(out, crl)) { - BIO_free(out); - ossl_raise(eX509CRLError, NULL); + BIO_free(out); + ossl_raise(eX509CRLError, NULL); } return ossl_membio2str(out); @@ -404,11 +407,11 @@ ossl_x509crl_to_pem(VALUE self) GetX509CRL(self, crl); if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eX509CRLError, NULL); + ossl_raise(eX509CRLError, NULL); } if (!PEM_write_bio_X509_CRL(out, crl)) { - BIO_free(out); - ossl_raise(eX509CRLError, NULL); + BIO_free(out); + ossl_raise(eX509CRLError, NULL); } return ossl_membio2str(out); @@ -422,11 +425,11 @@ ossl_x509crl_to_text(VALUE self) GetX509CRL(self, crl); if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eX509CRLError, NULL); + ossl_raise(eX509CRLError, NULL); } if (!X509_CRL_print(out, crl)) { - BIO_free(out); - ossl_raise(eX509CRLError, NULL); + BIO_free(out); + ossl_raise(eX509CRLError, NULL); } return ossl_membio2str(out); @@ -445,14 +448,10 @@ ossl_x509crl_get_extensions(VALUE self) GetX509CRL(self, crl); count = X509_CRL_get_ext_count(crl); - if (count < 0) { - OSSL_Debug("count < 0???"); - return rb_ary_new(); - } - ary = rb_ary_new2(count); + ary = rb_ary_new_capa(count); for (i=0; i<count; i++) { - ext = X509_CRL_get_ext(crl, i); /* NO DUP - don't free! */ - rb_ary_push(ary, ossl_x509ext_new(ext)); + ext = X509_CRL_get_ext(crl, i); /* NO DUP - don't free! */ + rb_ary_push(ary, ossl_x509ext_new(ext)); } return ary; @@ -471,16 +470,16 @@ ossl_x509crl_set_extensions(VALUE self, VALUE ary) Check_Type(ary, T_ARRAY); /* All ary members should be X509 Extensions */ for (i=0; i<RARRAY_LEN(ary); i++) { - OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext); + OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext); } GetX509CRL(self, crl); for (i = X509_CRL_get_ext_count(crl); i > 0; i--) X509_EXTENSION_free(X509_CRL_delete_ext(crl, 0)); for (i=0; i<RARRAY_LEN(ary); i++) { - ext = GetX509ExtPtr(RARRAY_AREF(ary, i)); /* NO NEED TO DUP */ - if (!X509_CRL_add_ext(crl, ext, -1)) { - ossl_raise(eX509CRLError, "X509_CRL_add_ext"); - } + ext = GetX509ExtPtr(RARRAY_AREF(ary, i)); /* NO NEED TO DUP */ + if (!X509_CRL_add_ext(crl, ext, -1)) { + ossl_raise(eX509CRLError, "X509_CRL_add_ext"); + } } return ary; @@ -495,7 +494,7 @@ ossl_x509crl_add_extension(VALUE self, VALUE extension) GetX509CRL(self, crl); ext = GetX509ExtPtr(extension); if (!X509_CRL_add_ext(crl, ext, -1)) { - ossl_raise(eX509CRLError, NULL); + ossl_raise(eX509CRLError, NULL); } return extension; @@ -507,12 +506,6 @@ ossl_x509crl_add_extension(VALUE self, VALUE extension) void Init_ossl_x509crl(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - mX509 = rb_define_module_under(mOSSL, "X509"); -#endif - eX509CRLError = rb_define_class_under(mX509, "CRLError", eOSSLError); cX509CRL = rb_define_class_under(mX509, "CRL", rb_cObject); diff --git a/ext/openssl/ossl_x509ext.c b/ext/openssl/ossl_x509ext.c index 192d09bd3f..ef66ecc3fe 100644 --- a/ext/openssl/ossl_x509ext.c +++ b/ext/openssl/ossl_x509ext.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -13,14 +13,14 @@ TypedData_Wrap_Struct((klass), &ossl_x509ext_type, 0) #define SetX509Ext(obj, ext) do { \ if (!(ext)) { \ - ossl_raise(rb_eRuntimeError, "EXT wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "EXT wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (ext); \ } while (0) #define GetX509Ext(obj, ext) do { \ TypedData_Get_Struct((obj), X509_EXTENSION, &ossl_x509ext_type, (ext)); \ if (!(ext)) { \ - ossl_raise(rb_eRuntimeError, "EXT wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "EXT wasn't initialized!"); \ } \ } while (0) #define MakeX509ExtFactory(klass, obj, ctx) do { \ @@ -33,7 +33,7 @@ #define GetX509ExtFactory(obj, ctx) do { \ TypedData_Get_Struct((obj), X509V3_CTX, &ossl_x509extfactory_type, (ctx)); \ if (!(ctx)) { \ - ossl_raise(rb_eRuntimeError, "CTX wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "CTX wasn't initialized!"); \ } \ } while (0) @@ -41,8 +41,8 @@ * Classes */ VALUE cX509Ext; -VALUE cX509ExtFactory; -VALUE eX509ExtError; +static VALUE cX509ExtFactory; +static VALUE eX509ExtError; static void ossl_x509ext_free(void *ptr) @@ -53,7 +53,7 @@ ossl_x509ext_free(void *ptr) static const rb_data_type_t ossl_x509ext_type = { "OpenSSL/X509/EXTENSION", { - 0, ossl_x509ext_free, + 0, ossl_x509ext_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -68,14 +68,9 @@ ossl_x509ext_new(X509_EXTENSION *ext) VALUE obj; obj = NewX509Ext(cX509Ext); - if (!ext) { - new = X509_EXTENSION_new(); - } else { - new = X509_EXTENSION_dup(ext); - } - if (!new) { - ossl_raise(eX509ExtError, NULL); - } + new = X509_EXTENSION_dup(ext); + if (!new) + ossl_raise(eX509ExtError, "X509_EXTENSION_dup"); SetX509Ext(obj, new); return obj; @@ -106,7 +101,7 @@ ossl_x509extfactory_free(void *ctx) static const rb_data_type_t ossl_x509extfactory_type = { "OpenSSL/X509/EXTENSION/Factory", { - 0, ossl_x509extfactory_free, + 0, ossl_x509extfactory_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -180,15 +175,15 @@ ossl_x509extfactory_initialize(int argc, VALUE *argv, VALUE self) /*GetX509ExtFactory(self, ctx);*/ rb_scan_args(argc, argv, "04", - &issuer_cert, &subject_cert, &subject_req, &crl); + &issuer_cert, &subject_cert, &subject_req, &crl); if (!NIL_P(issuer_cert)) - ossl_x509extfactory_set_issuer_cert(self, issuer_cert); + ossl_x509extfactory_set_issuer_cert(self, issuer_cert); if (!NIL_P(subject_cert)) - ossl_x509extfactory_set_subject_cert(self, subject_cert); + ossl_x509extfactory_set_subject_cert(self, subject_cert); if (!NIL_P(subject_req)) - ossl_x509extfactory_set_subject_req(self, subject_req); + ossl_x509extfactory_set_subject_req(self, subject_req); if (!NIL_P(crl)) - ossl_x509extfactory_set_crl(self, crl); + ossl_x509extfactory_set_crl(self, crl); return self; } @@ -218,7 +213,7 @@ ossl_x509extfactory_create_ext(int argc, VALUE *argv, VALUE self) oid_cstr = StringValueCStr(oid); nid = OBJ_ln2nid(oid_cstr); if (nid != NID_undef) - oid_cstr = OBJ_nid2sn(nid); + oid_cstr = OBJ_nid2sn(nid); valstr = rb_str_new2(RTEST(critical) ? "critical," : ""); rb_str_append(valstr, value); @@ -230,14 +225,10 @@ ossl_x509extfactory_create_ext(int argc, VALUE *argv, VALUE self) conf = NIL_P(rconf) ? NULL : GetConfig(rconf); X509V3_set_nconf(ctx, conf); -#if OSSL_OPENSSL_PREREQ(1, 1, 0) || OSSL_IS_LIBRESSL ext = X509V3_EXT_nconf(conf, ctx, oid_cstr, RSTRING_PTR(valstr)); -#else - ext = X509V3_EXT_nconf(conf, ctx, (char *)oid_cstr, RSTRING_PTR(valstr)); -#endif X509V3_set_ctx_nodb(ctx); if (!ext){ - ossl_raise(eX509ExtError, "%"PRIsVALUE" = %"PRIsVALUE, oid, valstr); + ossl_raise(eX509ExtError, "%"PRIsVALUE" = %"PRIsVALUE, oid, valstr); } SetX509Ext(obj, ext); @@ -255,7 +246,7 @@ ossl_x509ext_alloc(VALUE klass) obj = NewX509Ext(klass); if(!(ext = X509_EXTENSION_new())){ - ossl_raise(eX509ExtError, NULL); + ossl_raise(eX509ExtError, NULL); } SetX509Ext(obj, ext); @@ -283,14 +274,14 @@ ossl_x509ext_initialize(int argc, VALUE *argv, VALUE self) GetX509Ext(self, ext); if(rb_scan_args(argc, argv, "12", &oid, &value, &critical) == 1){ - oid = ossl_to_der_if_possible(oid); - StringValue(oid); - p = (unsigned char *)RSTRING_PTR(oid); - x = d2i_X509_EXTENSION(&ext, &p, RSTRING_LEN(oid)); - DATA_PTR(self) = ext; - if(!x) - ossl_raise(eX509ExtError, NULL); - return self; + oid = ossl_to_der_if_possible(oid); + StringValue(oid); + p = (unsigned char *)RSTRING_PTR(oid); + x = d2i_X509_EXTENSION(&ext, &p, RSTRING_LEN(oid)); + DATA_PTR(self) = ext; + if(!x) + ossl_raise(eX509ExtError, NULL); + return self; } rb_funcall(self, rb_intern("oid="), 1, oid); rb_funcall(self, rb_intern("value="), 1, value); @@ -299,6 +290,7 @@ ossl_x509ext_initialize(int argc, VALUE *argv, VALUE self) return self; } +/* :nodoc: */ static VALUE ossl_x509ext_initialize_copy(VALUE self, VALUE other) { @@ -310,7 +302,7 @@ ossl_x509ext_initialize_copy(VALUE self, VALUE other) ext_new = X509_EXTENSION_dup(ext_other); if (!ext_new) - ossl_raise(eX509ExtError, "X509_EXTENSION_dup"); + ossl_raise(eX509ExtError, "X509_EXTENSION_dup"); SetX509Ext(self, ext_new); X509_EXTENSION_free(ext); @@ -327,10 +319,10 @@ ossl_x509ext_set_oid(VALUE self, VALUE oid) GetX509Ext(self, ext); obj = OBJ_txt2obj(StringValueCStr(oid), 0); if (!obj) - ossl_raise(eX509ExtError, "OBJ_txt2obj"); + ossl_raise(eX509ExtError, "OBJ_txt2obj"); if (!X509_EXTENSION_set_object(ext, obj)) { - ASN1_OBJECT_free(obj); - ossl_raise(eX509ExtError, "X509_EXTENSION_set_object"); + ASN1_OBJECT_free(obj); + ossl_raise(eX509ExtError, "X509_EXTENSION_set_object"); } ASN1_OBJECT_free(obj); @@ -349,8 +341,8 @@ ossl_x509ext_set_value(VALUE self, VALUE data) asn1s = X509_EXTENSION_get_data(ext); if (!ASN1_OCTET_STRING_set(asn1s, (unsigned char *)RSTRING_PTR(data), - RSTRING_LENINT(data))) { - ossl_raise(eX509ExtError, "ASN1_OCTET_STRING_set"); + RSTRING_LENINT(data))) { + ossl_raise(eX509ExtError, "ASN1_OCTET_STRING_set"); } return data; @@ -367,27 +359,20 @@ ossl_x509ext_set_critical(VALUE self, VALUE flag) return flag; } +/* + * call-seq: + * ext.oid -> string + * + * Returns the OID of the extension. Returns the short name or the dotted + * decimal notation. + */ static VALUE ossl_x509ext_get_oid(VALUE obj) { X509_EXTENSION *ext; - ASN1_OBJECT *extobj; - BIO *out; - VALUE ret; - int nid; GetX509Ext(obj, ext); - extobj = X509_EXTENSION_get_object(ext); - if ((nid = OBJ_obj2nid(extobj)) != NID_undef) - ret = rb_str_new2(OBJ_nid2sn(nid)); - else{ - if (!(out = BIO_new(BIO_s_mem()))) - ossl_raise(eX509ExtError, NULL); - i2a_ASN1_OBJECT(out, extobj); - ret = ossl_membio2str(out); - } - - return ret; + return ossl_asn1obj_to_string(X509_EXTENSION_get_object(ext)); } static VALUE @@ -399,9 +384,9 @@ ossl_x509ext_get_value(VALUE obj) GetX509Ext(obj, ext); if (!(out = BIO_new(BIO_s_mem()))) - ossl_raise(eX509ExtError, NULL); + ossl_raise(eX509ExtError, NULL); if (!X509V3_EXT_print(out, ext, 0, 0)) - ASN1_STRING_print(out, (ASN1_STRING *)X509_EXTENSION_get_data(ext)); + ASN1_STRING_print(out, (ASN1_STRING *)X509_EXTENSION_get_data(ext)); ret = ossl_membio2str(out); return ret; @@ -415,9 +400,9 @@ ossl_x509ext_get_value_der(VALUE obj) GetX509Ext(obj, ext); if ((value = X509_EXTENSION_get_data(ext)) == NULL) - ossl_raise(eX509ExtError, NULL); + ossl_raise(eX509ExtError, NULL); - return rb_str_new((const char *)value->data, value->length); + return asn1str_to_str(value); } static VALUE @@ -439,11 +424,11 @@ ossl_x509ext_to_der(VALUE obj) GetX509Ext(obj, ext); if((len = i2d_X509_EXTENSION(ext, NULL)) <= 0) - ossl_raise(eX509ExtError, NULL); + ossl_raise(eX509ExtError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_X509_EXTENSION(ext, &p) < 0) - ossl_raise(eX509ExtError, NULL); + ossl_raise(eX509ExtError, NULL); ossl_str_adjust(str, p); return str; @@ -456,12 +441,6 @@ void Init_ossl_x509ext(void) { #undef rb_intern -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - mX509 = rb_define_module_under(mOSSL, "X509"); -#endif - eX509ExtError = rb_define_class_under(mX509, "ExtensionError", eOSSLError); cX509ExtFactory = rb_define_class_under(mX509, "ExtensionFactory", rb_cObject); diff --git a/ext/openssl/ossl_x509name.c b/ext/openssl/ossl_x509name.c index 9591912f70..5b3c3f7261 100644 --- a/ext/openssl/ossl_x509name.c +++ b/ext/openssl/ossl_x509name.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -13,27 +13,27 @@ TypedData_Wrap_Struct((klass), &ossl_x509name_type, 0) #define SetX509Name(obj, name) do { \ if (!(name)) { \ - ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \ + ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \ } \ RTYPEDDATA_DATA(obj) = (name); \ } while (0) #define GetX509Name(obj, name) do { \ TypedData_Get_Struct((obj), X509_NAME, &ossl_x509name_type, (name)); \ if (!(name)) { \ - ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \ + ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \ } \ } while (0) #define OBJECT_TYPE_TEMPLATE \ - rb_const_get(cX509Name, rb_intern("OBJECT_TYPE_TEMPLATE")) + rb_const_get(cX509Name, rb_intern("OBJECT_TYPE_TEMPLATE")) #define DEFAULT_OBJECT_TYPE \ - rb_const_get(cX509Name, rb_intern("DEFAULT_OBJECT_TYPE")) + rb_const_get(cX509Name, rb_intern("DEFAULT_OBJECT_TYPE")) /* * Classes */ -VALUE cX509Name; -VALUE eX509NameError; +static VALUE cX509Name; +static VALUE eX509NameError; static void ossl_x509name_free(void *ptr) @@ -44,7 +44,7 @@ ossl_x509name_free(void *ptr) static const rb_data_type_t ossl_x509name_type = { "OpenSSL/X509/NAME", { - 0, ossl_x509name_free, + 0, ossl_x509name_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -59,14 +59,9 @@ ossl_x509name_new(X509_NAME *name) VALUE obj; obj = NewX509Name(cX509Name); - if (!name) { - new = X509_NAME_new(); - } else { - new = X509_NAME_dup(name); - } - if (!new) { - ossl_raise(eX509NameError, NULL); - } + new = X509_NAME_dup(name); + if (!new) + ossl_raise(eX509NameError, "X509_NAME_dup"); SetX509Name(obj, new); return obj; @@ -93,7 +88,7 @@ ossl_x509name_alloc(VALUE klass) obj = NewX509Name(klass); if (!(name = X509_NAME_new())) { - ossl_raise(eX509NameError, NULL); + ossl_raise(eX509NameError, NULL); } SetX509Name(obj, name); @@ -150,33 +145,34 @@ ossl_x509name_initialize(int argc, VALUE *argv, VALUE self) GetX509Name(self, name); if (rb_scan_args(argc, argv, "02", &arg, &template) == 0) { - return self; + return self; } else { - VALUE tmp = rb_check_array_type(arg); - if (!NIL_P(tmp)) { - VALUE args; - if(NIL_P(template)) template = OBJECT_TYPE_TEMPLATE; - args = rb_ary_new3(2, self, template); - rb_block_call(tmp, rb_intern("each"), 0, 0, ossl_x509name_init_i, args); - } - else{ - const unsigned char *p; - VALUE str = ossl_to_der_if_possible(arg); - X509_NAME *x; - StringValue(str); - p = (unsigned char *)RSTRING_PTR(str); - x = d2i_X509_NAME(&name, &p, RSTRING_LEN(str)); - DATA_PTR(self) = name; - if(!x){ - ossl_raise(eX509NameError, NULL); - } - } + VALUE tmp = rb_check_array_type(arg); + if (!NIL_P(tmp)) { + VALUE args; + if(NIL_P(template)) template = OBJECT_TYPE_TEMPLATE; + args = rb_ary_new3(2, self, template); + rb_block_call(tmp, rb_intern("each"), 0, 0, ossl_x509name_init_i, args); + } + else{ + const unsigned char *p; + VALUE str = ossl_to_der_if_possible(arg); + X509_NAME *x; + StringValue(str); + p = (unsigned char *)RSTRING_PTR(str); + x = d2i_X509_NAME(&name, &p, RSTRING_LEN(str)); + DATA_PTR(self) = name; + if(!x){ + ossl_raise(eX509NameError, NULL); + } + } } return self; } +/* :nodoc: */ static VALUE ossl_x509name_initialize_copy(VALUE self, VALUE other) { @@ -188,7 +184,7 @@ ossl_x509name_initialize_copy(VALUE self, VALUE other) name_new = X509_NAME_dup(name_other); if (!name_new) - ossl_raise(eX509NameError, "X509_NAME_dup"); + ossl_raise(eX509NameError, "X509_NAME_dup"); SetX509Name(self, name_new); X509_NAME_free(name); @@ -225,8 +221,8 @@ VALUE ossl_x509name_add_entry(int argc, VALUE *argv, VALUE self) int loc = -1, set = 0; if (!kwargs_ids[0]) { - kwargs_ids[0] = rb_intern_const("loc"); - kwargs_ids[1] = rb_intern_const("set"); + kwargs_ids[0] = rb_intern_const("loc"); + kwargs_ids[1] = rb_intern_const("set"); } rb_scan_args(argc, argv, "21:", &oid, &value, &type, &opts); rb_get_kwargs(opts, kwargs_ids, 0, 2, kwargs); @@ -234,14 +230,14 @@ VALUE ossl_x509name_add_entry(int argc, VALUE *argv, VALUE self) StringValue(value); if(NIL_P(type)) type = rb_aref(OBJECT_TYPE_TEMPLATE, oid); if (kwargs[0] != Qundef) - loc = NUM2INT(kwargs[0]); + loc = NUM2INT(kwargs[0]); if (kwargs[1] != Qundef) - set = NUM2INT(kwargs[1]); + set = NUM2INT(kwargs[1]); GetX509Name(self, name); if (!X509_NAME_add_entry_by_txt(name, oid_name, NUM2INT(type), - (unsigned char *)RSTRING_PTR(value), - RSTRING_LENINT(value), loc, set)) - ossl_raise(eX509NameError, "X509_NAME_add_entry_by_txt"); + (unsigned char *)RSTRING_PTR(value), + RSTRING_LENINT(value), loc, set)) + ossl_raise(eX509NameError, "X509_NAME_add_entry_by_txt"); return self; } @@ -254,7 +250,7 @@ ossl_x509name_to_s_old(VALUE self) GetX509Name(self, name); buf = X509_NAME_oneline(name, NULL, 0); if (!buf) - ossl_raise(eX509NameError, "X509_NAME_oneline"); + ossl_raise(eX509NameError, "X509_NAME_oneline"); return ossl_buf2str(buf, rb_long2int(strlen(buf))); } @@ -268,11 +264,11 @@ x509name_print(VALUE self, unsigned long iflag) GetX509Name(self, name); out = BIO_new(BIO_s_mem()); if (!out) - ossl_raise(eX509NameError, NULL); + ossl_raise(eX509NameError, NULL); ret = X509_NAME_print_ex(out, name, 0, iflag); if (ret < 0 || (iflag == XN_FLAG_COMPAT && ret == 0)) { - BIO_free(out); - ossl_raise(eX509NameError, "X509_NAME_print_ex"); + BIO_free(out); + ossl_raise(eX509NameError, "X509_NAME_print_ex"); } return ossl_membio2str(out); } @@ -306,9 +302,9 @@ ossl_x509name_to_s(int argc, VALUE *argv, VALUE self) rb_check_arity(argc, 0, 1); /* name.to_s(nil) was allowed */ if (!argc || NIL_P(argv[0])) - return ossl_x509name_to_s_old(self); + return ossl_x509name_to_s_old(self); else - return x509name_print(self, NUM2ULONG(argv[0])); + return x509name_print(self, NUM2ULONG(argv[0])); } /* @@ -331,7 +327,7 @@ static VALUE ossl_x509name_inspect(VALUE self) { return rb_enc_sprintf(rb_utf8_encoding(), "#<%"PRIsVALUE" %"PRIsVALUE">", - rb_obj_class(self), ossl_x509name_to_utf8(self)); + rb_obj_class(self), ossl_x509name_to_utf8(self)); } /* @@ -345,38 +341,22 @@ static VALUE ossl_x509name_to_a(VALUE self) { X509_NAME *name; - X509_NAME_ENTRY *entry; - int i,entries,nid; - char long_name[512]; - const char *short_name; - VALUE ary, vname, ret; - ASN1_STRING *value; + int entries; + VALUE ret; GetX509Name(self, name); entries = X509_NAME_entry_count(name); - if (entries < 0) { - OSSL_Debug("name entries < 0!"); - return rb_ary_new(); - } - ret = rb_ary_new2(entries); - for (i=0; i<entries; i++) { - if (!(entry = X509_NAME_get_entry(name, i))) { - ossl_raise(eX509NameError, NULL); - } - if (!i2t_ASN1_OBJECT(long_name, sizeof(long_name), - X509_NAME_ENTRY_get_object(entry))) { - ossl_raise(eX509NameError, NULL); - } - nid = OBJ_ln2nid(long_name); - if (nid == NID_undef) { - vname = rb_str_new2((const char *) &long_name); - } else { - short_name = OBJ_nid2sn(nid); - vname = rb_str_new2(short_name); /*do not free*/ - } - value = X509_NAME_ENTRY_get_data(entry); - ary = rb_ary_new3(3, vname, asn1str_to_str(value), INT2NUM(value->type)); - rb_ary_push(ret, ary); + ret = rb_ary_new_capa(entries); + for (int i = 0; i < entries; i++) { + const X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i); + if (!entry) + ossl_raise(eX509NameError, "X509_NAME_get_entry"); + const ASN1_OBJECT *obj = X509_NAME_ENTRY_get_object(entry); + VALUE vname = ossl_asn1obj_to_string(obj); + const ASN1_STRING *data = X509_NAME_ENTRY_get_data(entry); + VALUE vdata = asn1str_to_str(data); + VALUE type = INT2NUM(ASN1_STRING_type(data)); + rb_ary_push(ret, rb_ary_new_from_args(3, vname, vdata, type)); } return ret; } @@ -407,7 +387,7 @@ ossl_x509name_cmp(VALUE self, VALUE other) int result; if (!rb_obj_is_kind_of(other, cX509Name)) - return Qnil; + return Qnil; result = ossl_x509name_cmp0(self, other); if (result < 0) return INT2FIX(-1); @@ -426,7 +406,7 @@ static VALUE ossl_x509name_eql(VALUE self, VALUE other) { if (!rb_obj_is_kind_of(other, cX509Name)) - return Qfalse; + return Qfalse; return ossl_x509name_cmp0(self, other) == 0 ? Qtrue : Qfalse; } @@ -486,11 +466,11 @@ ossl_x509name_to_der(VALUE self) GetX509Name(self, name); if((len = i2d_X509_NAME(name, NULL)) <= 0) - ossl_raise(eX509NameError, NULL); + ossl_raise(eX509NameError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_X509_NAME(name, &p) <= 0) - ossl_raise(eX509NameError, NULL); + ossl_raise(eX509NameError, NULL); ossl_str_adjust(str, p); return str; @@ -516,12 +496,6 @@ Init_ossl_x509name(void) #undef rb_intern VALUE utf8str, ptrstr, ia5str, hash; -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - mX509 = rb_define_module_under(mOSSL, "X509"); -#endif - id_aref = rb_intern("[]"); eX509NameError = rb_define_class_under(mX509, "NameError", eOSSLError); cX509Name = rb_define_class_under(mX509, "Name", rb_cObject); @@ -560,6 +534,7 @@ Init_ossl_x509name(void) rb_hash_aset(hash, rb_str_new2("DC"), ia5str); rb_hash_aset(hash, rb_str_new2("domainComponent"), ia5str); rb_hash_aset(hash, rb_str_new2("emailAddress"), ia5str); + rb_obj_freeze(hash); /* * The default object type template for name entries. diff --git a/ext/openssl/ossl_x509req.c b/ext/openssl/ossl_x509req.c index f058185151..433cc461a9 100644 --- a/ext/openssl/ossl_x509req.c +++ b/ext/openssl/ossl_x509req.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -13,22 +13,22 @@ TypedData_Wrap_Struct((klass), &ossl_x509req_type, 0) #define SetX509Req(obj, req) do { \ if (!(req)) { \ - ossl_raise(rb_eRuntimeError, "Req wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "Req wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (req); \ } while (0) #define GetX509Req(obj, req) do { \ TypedData_Get_Struct((obj), X509_REQ, &ossl_x509req_type, (req)); \ if (!(req)) { \ - ossl_raise(rb_eRuntimeError, "Req wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "Req wasn't initialized!"); \ } \ } while (0) /* * Classes */ -VALUE cX509Req; -VALUE eX509ReqError; +static VALUE cX509Req; +static VALUE eX509ReqError; static void ossl_x509req_free(void *ptr) @@ -39,7 +39,7 @@ ossl_x509req_free(void *ptr) static const rb_data_type_t ossl_x509req_type = { "OpenSSL/X509/REQ", { - 0, ossl_x509req_free, + 0, ossl_x509req_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -68,7 +68,7 @@ ossl_x509req_alloc(VALUE klass) obj = NewX509Req(klass); if (!(req = X509_REQ_new())) { - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } SetX509Req(obj, req); @@ -84,7 +84,7 @@ ossl_x509req_initialize(int argc, VALUE *argv, VALUE self) rb_check_frozen(self); if (rb_scan_args(argc, argv, "01", &arg) == 0) { - return self; + return self; } arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); @@ -103,6 +103,7 @@ ossl_x509req_initialize(int argc, VALUE *argv, VALUE self) return self; } +/* :nodoc: */ static VALUE ossl_x509req_copy(VALUE self, VALUE other) { @@ -113,7 +114,7 @@ ossl_x509req_copy(VALUE self, VALUE other) GetX509Req(self, a); GetX509Req(other, b); if (!(req = X509_REQ_dup(b))) { - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } X509_REQ_free(a); DATA_PTR(self) = req; @@ -129,11 +130,11 @@ ossl_x509req_to_pem(VALUE self) GetX509Req(self, req); if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } if (!PEM_write_bio_X509_REQ(out, req)) { - BIO_free(out); - ossl_raise(eX509ReqError, NULL); + BIO_free(out); + ossl_raise(eX509ReqError, NULL); } return ossl_membio2str(out); @@ -149,11 +150,11 @@ ossl_x509req_to_der(VALUE self) GetX509Req(self, req); if ((len = i2d_X509_REQ(req, NULL)) <= 0) - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_X509_REQ(req, &p) <= 0) - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); ossl_str_adjust(str, p); return str; @@ -167,11 +168,11 @@ ossl_x509req_to_text(VALUE self) GetX509Req(self, req); if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } if (!X509_REQ_print(out, req)) { - BIO_free(out); - ossl_raise(eX509ReqError, NULL); + BIO_free(out); + ossl_raise(eX509ReqError, NULL); } return ossl_membio2str(out); @@ -190,7 +191,7 @@ ossl_x509req_to_x509(VALUE self, VALUE days, VALUE key) GetX509Req(self, req); ... if (!(x509 = X509_REQ_to_X509(req, d, pkey))) { - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } return ossl_x509_new(x509); @@ -216,11 +217,11 @@ ossl_x509req_set_version(VALUE self, VALUE version) long ver; if ((ver = NUM2LONG(version)) < 0) { - ossl_raise(eX509ReqError, "version must be >= 0!"); + ossl_raise(eX509ReqError, "version must be >= 0!"); } GetX509Req(self, req); if (!X509_REQ_set_version(req, ver)) { - ossl_raise(eX509ReqError, "X509_REQ_set_version"); + ossl_raise(eX509ReqError, "X509_REQ_set_version"); } return version; @@ -234,7 +235,7 @@ ossl_x509req_get_subject(VALUE self) GetX509Req(self, req); if (!(name = X509_REQ_get_subject_name(req))) { /* NO DUP - don't free */ - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } return ossl_x509name_new(name); @@ -248,31 +249,32 @@ ossl_x509req_set_subject(VALUE self, VALUE subject) GetX509Req(self, req); /* DUPs name */ if (!X509_REQ_set_subject_name(req, GetX509NamePtr(subject))) { - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } return subject; } +/* + * call-seq: + * req.signature_algorithm -> string + * + * Returns the signature algorithm used to sign this request. + * + * Returns the long name of the signature algorithm, or the dotted decimal + * notation if \OpenSSL does not define a long name for it. + */ static VALUE ossl_x509req_get_signature_algorithm(VALUE self) { X509_REQ *req; const X509_ALGOR *alg; - BIO *out; + const ASN1_OBJECT *obj; GetX509Req(self, req); - - if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eX509ReqError, NULL); - } X509_REQ_get0_signature(req, NULL, &alg); - if (!i2a_ASN1_OBJECT(out, alg->algorithm)) { - BIO_free(out); - ossl_raise(eX509ReqError, NULL); - } - - return ossl_membio2str(out); + X509_ALGOR_get0(&obj, NULL, NULL, alg); + return ossl_asn1obj_to_string_long_name(obj); } static VALUE @@ -283,10 +285,10 @@ ossl_x509req_get_public_key(VALUE self) GetX509Req(self, req); if (!(pkey = X509_REQ_get_pubkey(req))) { /* adds reference */ - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } - return ossl_pkey_new(pkey); /* NO DUP - OK */ + return ossl_pkey_wrap(pkey); } static VALUE @@ -299,7 +301,7 @@ ossl_x509req_set_public_key(VALUE self, VALUE key) pkey = GetPKeyPtr(key); ossl_pkey_check_public_key(pkey); if (!X509_REQ_set_pubkey(req, pkey)) - ossl_raise(eX509ReqError, "X509_REQ_set_pubkey"); + ossl_raise(eX509ReqError, "X509_REQ_set_pubkey"); return key; } @@ -309,13 +311,14 @@ ossl_x509req_sign(VALUE self, VALUE key, VALUE digest) X509_REQ *req; EVP_PKEY *pkey; const EVP_MD *md; + VALUE md_holder; GetX509Req(self, req); pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ - md = ossl_evp_get_digestbyname(digest); - if (!X509_REQ_sign(req, pkey, md)) { - ossl_raise(eX509ReqError, NULL); - } + /* NULL needed for some key types, e.g. Ed25519 */ + md = NIL_P(digest) ? NULL : ossl_evp_md_fetch(digest, &md_holder); + if (!X509_REQ_sign(req, pkey, md)) + ossl_raise(eX509ReqError, "X509_REQ_sign"); return self; } @@ -334,12 +337,12 @@ ossl_x509req_verify(VALUE self, VALUE key) ossl_pkey_check_public_key(pkey); switch (X509_REQ_verify(req, pkey)) { case 1: - return Qtrue; + return Qtrue; case 0: - ossl_clear_error(); - return Qfalse; + ossl_clear_error(); + return Qfalse; default: - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } } @@ -355,13 +358,13 @@ ossl_x509req_get_attributes(VALUE self) count = X509_REQ_get_attr_count(req); if (count < 0) { - OSSL_Debug("count < 0???"); - return rb_ary_new(); + OSSL_Debug("count < 0???"); + return rb_ary_new(); } ary = rb_ary_new2(count); for (i=0; i<count; i++) { - attr = X509_REQ_get_attr(req, i); - rb_ary_push(ary, ossl_x509attr_new(attr)); + attr = X509_REQ_get_attr(req, i); + rb_ary_push(ary, ossl_x509attr_new(attr)); } return ary; @@ -377,17 +380,17 @@ ossl_x509req_set_attributes(VALUE self, VALUE ary) Check_Type(ary, T_ARRAY); for (i=0;i<RARRAY_LEN(ary); i++) { - OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Attr); + OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Attr); } GetX509Req(self, req); for (i = X509_REQ_get_attr_count(req); i > 0; i--) X509_ATTRIBUTE_free(X509_REQ_delete_attr(req, 0)); for (i=0;i<RARRAY_LEN(ary); i++) { - item = RARRAY_AREF(ary, i); - attr = GetX509AttrPtr(item); - if (!X509_REQ_add1_attr(req, attr)) { - ossl_raise(eX509ReqError, "X509_REQ_add1_attr"); - } + item = RARRAY_AREF(ary, i); + attr = GetX509AttrPtr(item); + if (!X509_REQ_add1_attr(req, attr)) { + ossl_raise(eX509ReqError, "X509_REQ_add1_attr"); + } } return ary; } @@ -399,7 +402,7 @@ ossl_x509req_add_attribute(VALUE self, VALUE attr) GetX509Req(self, req); if (!X509_REQ_add1_attr(req, GetX509AttrPtr(attr))) { - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } return attr; @@ -411,12 +414,6 @@ ossl_x509req_add_attribute(VALUE self, VALUE attr) void Init_ossl_x509req(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - mX509 = rb_define_module_under(mOSSL, "X509"); -#endif - eX509ReqError = rb_define_class_under(mX509, "RequestError", eOSSLError); cX509Req = rb_define_class_under(mX509, "Request", rb_cObject); diff --git a/ext/openssl/ossl_x509revoked.c b/ext/openssl/ossl_x509revoked.c index 108447c868..b88c390c72 100644 --- a/ext/openssl/ossl_x509revoked.c +++ b/ext/openssl/ossl_x509revoked.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -13,14 +13,14 @@ TypedData_Wrap_Struct((klass), &ossl_x509rev_type, 0) #define SetX509Rev(obj, rev) do { \ if (!(rev)) { \ - ossl_raise(rb_eRuntimeError, "REV wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "REV wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (rev); \ } while (0) #define GetX509Rev(obj, rev) do { \ TypedData_Get_Struct((obj), X509_REVOKED, &ossl_x509rev_type, (rev)); \ if (!(rev)) { \ - ossl_raise(rb_eRuntimeError, "REV wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "REV wasn't initialized!"); \ } \ } while (0) @@ -28,7 +28,7 @@ * Classes */ VALUE cX509Rev; -VALUE eX509RevError; +static VALUE eX509RevError; static void ossl_x509rev_free(void *ptr) @@ -39,7 +39,7 @@ ossl_x509rev_free(void *ptr) static const rb_data_type_t ossl_x509rev_type = { "OpenSSL/X509/REV", { - 0, ossl_x509rev_free, + 0, ossl_x509rev_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -54,14 +54,9 @@ ossl_x509revoked_new(X509_REVOKED *rev) VALUE obj; obj = NewX509Rev(cX509Rev); - if (!rev) { - new = X509_REVOKED_new(); - } else { - new = X509_REVOKED_dup(rev); - } - if (!new) { - ossl_raise(eX509RevError, NULL); - } + new = X509_REVOKED_dup(rev); + if (!new) + ossl_raise(eX509RevError, "X509_REVOKED_dup"); SetX509Rev(obj, new); return obj; @@ -74,7 +69,7 @@ DupX509RevokedPtr(VALUE obj) GetX509Rev(obj, rev); if (!(new = X509_REVOKED_dup(rev))) { - ossl_raise(eX509RevError, NULL); + ossl_raise(eX509RevError, NULL); } return new; @@ -91,7 +86,7 @@ ossl_x509revoked_alloc(VALUE klass) obj = NewX509Rev(klass); if (!(rev = X509_REVOKED_new())) { - ossl_raise(eX509RevError, NULL); + ossl_raise(eX509RevError, NULL); } SetX509Rev(obj, rev); @@ -105,6 +100,7 @@ ossl_x509revoked_initialize(int argc, VALUE *argv, VALUE self) return self; } +/* :nodoc: */ static VALUE ossl_x509revoked_initialize_copy(VALUE self, VALUE other) { @@ -116,7 +112,7 @@ ossl_x509revoked_initialize_copy(VALUE self, VALUE other) rev_new = X509_REVOKED_dup(rev_other); if (!rev_new) - ossl_raise(eX509RevError, "X509_REVOKED_dup"); + ossl_raise(eX509RevError, "X509_REVOKED_dup"); SetX509Rev(self, rev_new); X509_REVOKED_free(rev); @@ -143,8 +139,8 @@ ossl_x509revoked_set_serial(VALUE self, VALUE num) GetX509Rev(self, rev); asn1int = num_to_asn1integer(num, NULL); if (!X509_REVOKED_set_serialNumber(rev, asn1int)) { - ASN1_INTEGER_free(asn1int); - ossl_raise(eX509RevError, "X509_REVOKED_set_serialNumber"); + ASN1_INTEGER_free(asn1int); + ossl_raise(eX509RevError, "X509_REVOKED_set_serialNumber"); } ASN1_INTEGER_free(asn1int); @@ -160,7 +156,7 @@ ossl_x509revoked_get_time(VALUE self) GetX509Rev(self, rev); time = X509_REVOKED_get0_revocationDate(rev); if (!time) - return Qnil; + return Qnil; return asn1time_to_time(time); } @@ -174,8 +170,8 @@ ossl_x509revoked_set_time(VALUE self, VALUE time) GetX509Rev(self, rev); asn1time = ossl_x509_time_adjust(NULL, time); if (!X509_REVOKED_set_revocationDate(rev, asn1time)) { - ASN1_TIME_free(asn1time); - ossl_raise(eX509RevError, "X509_REVOKED_set_revocationDate"); + ASN1_TIME_free(asn1time); + ossl_raise(eX509RevError, "X509_REVOKED_set_revocationDate"); } ASN1_TIME_free(asn1time); @@ -194,14 +190,10 @@ ossl_x509revoked_get_extensions(VALUE self) GetX509Rev(self, rev); count = X509_REVOKED_get_ext_count(rev); - if (count < 0) { - OSSL_Debug("count < 0???"); - return rb_ary_new(); - } - ary = rb_ary_new2(count); + ary = rb_ary_new_capa(count); for (i=0; i<count; i++) { - ext = X509_REVOKED_get_ext(rev, i); - rb_ary_push(ary, ossl_x509ext_new(ext)); + ext = X509_REVOKED_get_ext(rev, i); + rb_ary_push(ary, ossl_x509ext_new(ext)); } return ary; @@ -220,17 +212,17 @@ ossl_x509revoked_set_extensions(VALUE self, VALUE ary) Check_Type(ary, T_ARRAY); for (i=0; i<RARRAY_LEN(ary); i++) { - OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext); + OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext); } GetX509Rev(self, rev); for (i = X509_REVOKED_get_ext_count(rev); i > 0; i--) X509_EXTENSION_free(X509_REVOKED_delete_ext(rev, 0)); for (i=0; i<RARRAY_LEN(ary); i++) { - item = RARRAY_AREF(ary, i); - ext = GetX509ExtPtr(item); - if(!X509_REVOKED_add_ext(rev, ext, -1)) { - ossl_raise(eX509RevError, "X509_REVOKED_add_ext"); - } + item = RARRAY_AREF(ary, i); + ext = GetX509ExtPtr(item); + if(!X509_REVOKED_add_ext(rev, ext, -1)) { + ossl_raise(eX509RevError, "X509_REVOKED_add_ext"); + } } return ary; @@ -243,7 +235,7 @@ ossl_x509revoked_add_extension(VALUE self, VALUE ext) GetX509Rev(self, rev); if (!X509_REVOKED_add_ext(rev, GetX509ExtPtr(ext), -1)) { - ossl_raise(eX509RevError, NULL); + ossl_raise(eX509RevError, NULL); } return ext; @@ -260,11 +252,11 @@ ossl_x509revoked_to_der(VALUE self) GetX509Rev(self, rev); len = i2d_X509_REVOKED(rev, NULL); if (len <= 0) - ossl_raise(eX509RevError, "i2d_X509_REVOKED"); + ossl_raise(eX509RevError, "i2d_X509_REVOKED"); str = rb_str_new(NULL, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_X509_REVOKED(rev, &p) <= 0) - ossl_raise(eX509RevError, "i2d_X509_REVOKED"); + ossl_raise(eX509RevError, "i2d_X509_REVOKED"); ossl_str_adjust(str, p); return str; } @@ -275,12 +267,6 @@ ossl_x509revoked_to_der(VALUE self) void Init_ossl_x509revoked(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - mX509 = rb_define_module_under(mOSSL, "X509"); -#endif - eX509RevError = rb_define_class_under(mX509, "RevokedError", eOSSLError); cX509Rev = rb_define_class_under(mX509, "Revoked", rb_cObject); diff --git a/ext/openssl/ossl_x509store.c b/ext/openssl/ossl_x509store.c index f27381ca90..be1458cec5 100644 --- a/ext/openssl/ossl_x509store.c +++ b/ext/openssl/ossl_x509store.c @@ -5,7 +5,7 @@ */ /* * This program is licensed under the same licence as Ruby. - * (See the file 'LICENCE'.) + * (See the file 'COPYING'.) */ #include "ossl.h" @@ -13,14 +13,14 @@ TypedData_Wrap_Struct((klass), &ossl_x509store_type, 0) #define SetX509Store(obj, st) do { \ if (!(st)) { \ - ossl_raise(rb_eRuntimeError, "STORE wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "STORE wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (st); \ } while (0) #define GetX509Store(obj, st) do { \ TypedData_Get_Struct((obj), X509_STORE, &ossl_x509store_type, (st)); \ if (!(st)) { \ - ossl_raise(rb_eRuntimeError, "STORE wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "STORE wasn't initialized!"); \ } \ } while (0) @@ -28,14 +28,14 @@ TypedData_Wrap_Struct((klass), &ossl_x509stctx_type, 0) #define SetX509StCtx(obj, ctx) do { \ if (!(ctx)) { \ - ossl_raise(rb_eRuntimeError, "STORE_CTX wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "STORE_CTX wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (ctx); \ } while (0) #define GetX509StCtx(obj, ctx) do { \ TypedData_Get_Struct((obj), X509_STORE_CTX, &ossl_x509stctx_type, (ctx)); \ if (!(ctx)) { \ - ossl_raise(rb_eRuntimeError, "STORE_CTX is out of scope!"); \ + ossl_raise(rb_eRuntimeError, "STORE_CTX is out of scope!"); \ } \ } while (0) @@ -62,7 +62,7 @@ call_verify_cb_proc(VALUE arg) { struct ossl_verify_cb_args *args = (struct ossl_verify_cb_args *)arg; return rb_funcall(args->proc, rb_intern("call"), 2, - args->preverify_ok, args->store_ctx); + args->preverify_ok, args->store_ctx); } int @@ -73,33 +73,33 @@ ossl_verify_cb_call(VALUE proc, int ok, X509_STORE_CTX *ctx) int state; if (NIL_P(proc)) - return ok; + return ok; ret = Qfalse; rctx = rb_protect(ossl_x509stctx_new_i, (VALUE)ctx, &state); if (state) { - rb_set_errinfo(Qnil); - rb_warn("StoreContext initialization failure"); + rb_set_errinfo(Qnil); + rb_warn("StoreContext initialization failure"); } else { - args.proc = proc; - args.preverify_ok = ok ? Qtrue : Qfalse; - args.store_ctx = rctx; - ret = rb_protect(call_verify_cb_proc, (VALUE)&args, &state); - if (state) { - rb_set_errinfo(Qnil); - rb_warn("exception in verify_callback is ignored"); - } - RTYPEDDATA_DATA(rctx) = NULL; + args.proc = proc; + args.preverify_ok = ok ? Qtrue : Qfalse; + args.store_ctx = rctx; + ret = rb_protect(call_verify_cb_proc, (VALUE)&args, &state); + if (state) { + rb_set_errinfo(Qnil); + rb_warn("exception in verify_callback is ignored"); + } + RTYPEDDATA_DATA(rctx) = NULL; } if (ret == Qtrue) { - X509_STORE_CTX_set_error(ctx, X509_V_OK); - ok = 1; + X509_STORE_CTX_set_error(ctx, X509_V_OK); + ok = 1; } else { - if (X509_STORE_CTX_get_error(ctx) == X509_V_OK) - X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REJECTED); - ok = 0; + if (X509_STORE_CTX_get_error(ctx) == X509_V_OK) + X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REJECTED); + ok = 0; } return ok; @@ -108,9 +108,9 @@ ossl_verify_cb_call(VALUE proc, int ok, X509_STORE_CTX *ctx) /* * Classes */ -VALUE cX509Store; -VALUE cX509StoreContext; -VALUE eX509StoreError; +static VALUE cX509Store; +static VALUE cX509StoreContext; +static VALUE eX509StoreError; static void ossl_x509store_mark(void *ptr) @@ -159,10 +159,10 @@ x509store_verify_cb(int ok, X509_STORE_CTX *ctx) proc = (VALUE)X509_STORE_CTX_get_ex_data(ctx, stctx_ex_verify_cb_idx); if (!proc) - proc = (VALUE)X509_STORE_get_ex_data(X509_STORE_CTX_get0_store(ctx), - store_ex_verify_cb_idx); + proc = (VALUE)X509_STORE_get_ex_data(X509_STORE_CTX_get0_store(ctx), + store_ex_verify_cb_idx); if (!proc) - return ok; + return ok; return ossl_verify_cb_call(proc, ok, ctx); } @@ -191,8 +191,8 @@ ossl_x509store_set_vfy_cb(VALUE self, VALUE cb) GetX509Store(self, store); rb_iv_set(self, "@verify_callback", cb); - // We don't need to trigger a write barrier because `rb_iv_set` did it. X509_STORE_set_ex_data(store, store_ex_verify_cb_idx, (void *)cb); + RB_OBJ_WRITTEN(self, Qundef, cb); return cb; } @@ -212,10 +212,6 @@ ossl_x509store_initialize(int argc, VALUE *argv, VALUE self) GetX509Store(self, store); if (argc != 0) rb_warn("OpenSSL::X509::Store.new does not take any arguments"); -#if !defined(HAVE_OPAQUE_OPENSSL) - /* [Bug #405] [Bug #1678] [Bug #3000]; already fixed? */ - store->ex_data.sk = NULL; -#endif X509_STORE_set_verify_cb(store, x509store_verify_cb); ossl_x509store_set_vfy_cb(self, Qnil); @@ -223,7 +219,6 @@ ossl_x509store_initialize(int argc, VALUE *argv, VALUE self) rb_iv_set(self, "@error", Qnil); rb_iv_set(self, "@error_string", Qnil); rb_iv_set(self, "@chain", Qnil); - rb_iv_set(self, "@time", Qnil); return self; } @@ -329,7 +324,12 @@ ossl_x509store_set_trust(VALUE self, VALUE trust) static VALUE ossl_x509store_set_time(VALUE self, VALUE time) { - rb_iv_set(self, "@time", time); + X509_STORE *store; + X509_VERIFY_PARAM *param; + + GetX509Store(self, store); + param = X509_STORE_get0_param(store); + X509_VERIFY_PARAM_set_time(param, NUM2LONG(rb_Integer(time))); return time; } @@ -357,15 +357,6 @@ ossl_x509store_add_file(VALUE self, VALUE file) ossl_raise(eX509StoreError, "X509_STORE_add_lookup"); if (X509_LOOKUP_load_file(lookup, path, X509_FILETYPE_PEM) != 1) ossl_raise(eX509StoreError, "X509_LOOKUP_load_file"); -#if OPENSSL_VERSION_NUMBER < 0x10101000 || defined(LIBRESSL_VERSION_NUMBER) - /* - * X509_load_cert_crl_file() which is called from X509_LOOKUP_load_file() - * did not check the return value of X509_STORE_add_{cert,crl}(), leaking - * "cert already in hash table" errors on the error queue, if duplicate - * certificates are found. This will be fixed by OpenSSL 1.1.1. - */ - ossl_clear_error(); -#endif return self; } @@ -493,7 +484,7 @@ ossl_x509store_verify(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "11", &cert, &chain); ctx = rb_funcall(cX509StoreContext, rb_intern("new"), 3, self, cert, chain); proc = rb_block_given_p() ? rb_block_proc() : - rb_iv_get(self, "@verify_callback"); + rb_iv_get(self, "@verify_callback"); rb_iv_set(ctx, "@verify_callback", proc); result = rb_funcall(ctx, rb_intern("verify"), 0); @@ -522,9 +513,9 @@ ossl_x509stctx_free(void *ptr) { X509_STORE_CTX *ctx = ptr; if (X509_STORE_CTX_get0_untrusted(ctx)) - sk_X509_pop_free(X509_STORE_CTX_get0_untrusted(ctx), X509_free); + sk_X509_pop_free(X509_STORE_CTX_get0_untrusted(ctx), X509_free); if (X509_STORE_CTX_get0_cert(ctx)) - X509_free(X509_STORE_CTX_get0_cert(ctx)); + X509_free(X509_STORE_CTX_get0_cert(ctx)); X509_STORE_CTX_free(ctx); } @@ -564,7 +555,6 @@ ossl_x509stctx_new(X509_STORE_CTX *ctx) static VALUE ossl_x509stctx_set_flags(VALUE, VALUE); static VALUE ossl_x509stctx_set_purpose(VALUE, VALUE); static VALUE ossl_x509stctx_set_trust(VALUE, VALUE); -static VALUE ossl_x509stctx_set_time(VALUE, VALUE); /* * call-seq: @@ -575,7 +565,7 @@ static VALUE ossl_x509stctx_set_time(VALUE, VALUE); static VALUE ossl_x509stctx_initialize(int argc, VALUE *argv, VALUE self) { - VALUE store, cert, chain, t; + VALUE store, cert, chain; X509_STORE_CTX *ctx; X509_STORE *x509st; X509 *x509 = NULL; @@ -599,8 +589,6 @@ ossl_x509stctx_initialize(int argc, VALUE *argv, VALUE self) sk_X509_pop_free(x509s, X509_free); ossl_raise(eX509StoreError, "X509_STORE_CTX_init"); } - if (!NIL_P(t = rb_iv_get(store, "@time"))) - ossl_x509stctx_set_time(self, t); rb_iv_set(self, "@verify_callback", rb_iv_get(store, "@verify_callback")); rb_iv_set(self, "@cert", cert); @@ -623,6 +611,7 @@ ossl_x509stctx_verify(VALUE self) GetX509StCtx(self, ctx); VALUE cb = rb_iv_get(self, "@verify_callback"); X509_STORE_CTX_set_ex_data(ctx, stctx_ex_verify_cb_idx, (void *)cb); + RB_OBJ_WRITTEN(self, Qundef, cb); switch (X509_verify_cert(ctx)) { case 1: @@ -631,7 +620,7 @@ ossl_x509stctx_verify(VALUE self) ossl_clear_error(); return Qfalse; default: - ossl_raise(eX509CertError, "X509_verify_cert"); + ossl_raise(eX509StoreError, "X509_verify_cert"); } } @@ -747,10 +736,14 @@ static VALUE ossl_x509stctx_get_curr_cert(VALUE self) { X509_STORE_CTX *ctx; + X509 *x509; GetX509StCtx(self, ctx); + x509 = X509_STORE_CTX_get_current_cert(ctx); + if (!x509) + return Qnil; - return ossl_x509_new(X509_STORE_CTX_get_current_cert(ctx)); + return ossl_x509_new(x509); } /* @@ -770,7 +763,7 @@ ossl_x509stctx_get_curr_crl(VALUE self) GetX509StCtx(self, ctx); crl = X509_STORE_CTX_get0_current_crl(ctx); if (!crl) - return Qnil; + return Qnil; return ossl_x509crl_new(crl); } @@ -866,19 +859,13 @@ void Init_ossl_x509store(void) { #undef rb_intern -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - mX509 = rb_define_module_under(mOSSL, "X509"); -#endif - /* Register ext_data slot for verify callback Proc */ stctx_ex_verify_cb_idx = X509_STORE_CTX_get_ex_new_index(0, (void *)"stctx_ex_verify_cb_idx", 0, 0, 0); if (stctx_ex_verify_cb_idx < 0) - ossl_raise(eOSSLError, "X509_STORE_CTX_get_ex_new_index"); + ossl_raise(eOSSLError, "X509_STORE_CTX_get_ex_new_index"); store_ex_verify_cb_idx = X509_STORE_get_ex_new_index(0, (void *)"store_ex_verify_cb_idx", 0, 0, 0); if (store_ex_verify_cb_idx < 0) - ossl_raise(eOSSLError, "X509_STORE_get_ex_new_index"); + ossl_raise(eOSSLError, "X509_STORE_get_ex_new_index"); eX509StoreError = rb_define_class_under(mX509, "StoreError", eOSSLError); diff --git a/ext/pathname/depend b/ext/pathname/depend deleted file mode 100644 index 5dd8b042de..0000000000 --- a/ext/pathname/depend +++ /dev/null @@ -1,173 +0,0 @@ -# AUTOGENERATED DEPENDENCIES START -pathname.o: $(RUBY_EXTCONF_H) -pathname.o: $(arch_hdrdir)/ruby/config.h -pathname.o: $(hdrdir)/ruby.h -pathname.o: $(hdrdir)/ruby/assert.h -pathname.o: $(hdrdir)/ruby/backward.h -pathname.o: $(hdrdir)/ruby/backward/2/assume.h -pathname.o: $(hdrdir)/ruby/backward/2/attributes.h -pathname.o: $(hdrdir)/ruby/backward/2/bool.h -pathname.o: $(hdrdir)/ruby/backward/2/inttypes.h -pathname.o: $(hdrdir)/ruby/backward/2/limits.h -pathname.o: $(hdrdir)/ruby/backward/2/long_long.h -pathname.o: $(hdrdir)/ruby/backward/2/stdalign.h -pathname.o: $(hdrdir)/ruby/backward/2/stdarg.h -pathname.o: $(hdrdir)/ruby/defines.h -pathname.o: $(hdrdir)/ruby/encoding.h -pathname.o: $(hdrdir)/ruby/intern.h -pathname.o: $(hdrdir)/ruby/internal/abi.h -pathname.o: $(hdrdir)/ruby/internal/anyargs.h -pathname.o: $(hdrdir)/ruby/internal/arithmetic.h -pathname.o: $(hdrdir)/ruby/internal/arithmetic/char.h -pathname.o: $(hdrdir)/ruby/internal/arithmetic/double.h -pathname.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h -pathname.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h -pathname.o: $(hdrdir)/ruby/internal/arithmetic/int.h -pathname.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h -pathname.o: $(hdrdir)/ruby/internal/arithmetic/long.h -pathname.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h -pathname.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h -pathname.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h -pathname.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h -pathname.o: $(hdrdir)/ruby/internal/arithmetic/short.h -pathname.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h -pathname.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h -pathname.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h -pathname.o: $(hdrdir)/ruby/internal/assume.h -pathname.o: $(hdrdir)/ruby/internal/attr/alloc_size.h -pathname.o: $(hdrdir)/ruby/internal/attr/artificial.h -pathname.o: $(hdrdir)/ruby/internal/attr/cold.h -pathname.o: $(hdrdir)/ruby/internal/attr/const.h -pathname.o: $(hdrdir)/ruby/internal/attr/constexpr.h -pathname.o: $(hdrdir)/ruby/internal/attr/deprecated.h -pathname.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h -pathname.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h -pathname.o: $(hdrdir)/ruby/internal/attr/error.h -pathname.o: $(hdrdir)/ruby/internal/attr/flag_enum.h -pathname.o: $(hdrdir)/ruby/internal/attr/forceinline.h -pathname.o: $(hdrdir)/ruby/internal/attr/format.h -pathname.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h -pathname.o: $(hdrdir)/ruby/internal/attr/noalias.h -pathname.o: $(hdrdir)/ruby/internal/attr/nodiscard.h -pathname.o: $(hdrdir)/ruby/internal/attr/noexcept.h -pathname.o: $(hdrdir)/ruby/internal/attr/noinline.h -pathname.o: $(hdrdir)/ruby/internal/attr/nonnull.h -pathname.o: $(hdrdir)/ruby/internal/attr/noreturn.h -pathname.o: $(hdrdir)/ruby/internal/attr/packed_struct.h -pathname.o: $(hdrdir)/ruby/internal/attr/pure.h -pathname.o: $(hdrdir)/ruby/internal/attr/restrict.h -pathname.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h -pathname.o: $(hdrdir)/ruby/internal/attr/warning.h -pathname.o: $(hdrdir)/ruby/internal/attr/weakref.h -pathname.o: $(hdrdir)/ruby/internal/cast.h -pathname.o: $(hdrdir)/ruby/internal/compiler_is.h -pathname.o: $(hdrdir)/ruby/internal/compiler_is/apple.h -pathname.o: $(hdrdir)/ruby/internal/compiler_is/clang.h -pathname.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h -pathname.o: $(hdrdir)/ruby/internal/compiler_is/intel.h -pathname.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h -pathname.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h -pathname.o: $(hdrdir)/ruby/internal/compiler_since.h -pathname.o: $(hdrdir)/ruby/internal/config.h -pathname.o: $(hdrdir)/ruby/internal/constant_p.h -pathname.o: $(hdrdir)/ruby/internal/core.h -pathname.o: $(hdrdir)/ruby/internal/core/rarray.h -pathname.o: $(hdrdir)/ruby/internal/core/rbasic.h -pathname.o: $(hdrdir)/ruby/internal/core/rbignum.h -pathname.o: $(hdrdir)/ruby/internal/core/rclass.h -pathname.o: $(hdrdir)/ruby/internal/core/rdata.h -pathname.o: $(hdrdir)/ruby/internal/core/rfile.h -pathname.o: $(hdrdir)/ruby/internal/core/rhash.h -pathname.o: $(hdrdir)/ruby/internal/core/robject.h -pathname.o: $(hdrdir)/ruby/internal/core/rregexp.h -pathname.o: $(hdrdir)/ruby/internal/core/rstring.h -pathname.o: $(hdrdir)/ruby/internal/core/rstruct.h -pathname.o: $(hdrdir)/ruby/internal/core/rtypeddata.h -pathname.o: $(hdrdir)/ruby/internal/ctype.h -pathname.o: $(hdrdir)/ruby/internal/dllexport.h -pathname.o: $(hdrdir)/ruby/internal/dosish.h -pathname.o: $(hdrdir)/ruby/internal/encoding/coderange.h -pathname.o: $(hdrdir)/ruby/internal/encoding/ctype.h -pathname.o: $(hdrdir)/ruby/internal/encoding/encoding.h -pathname.o: $(hdrdir)/ruby/internal/encoding/pathname.h -pathname.o: $(hdrdir)/ruby/internal/encoding/re.h -pathname.o: $(hdrdir)/ruby/internal/encoding/sprintf.h -pathname.o: $(hdrdir)/ruby/internal/encoding/string.h -pathname.o: $(hdrdir)/ruby/internal/encoding/symbol.h -pathname.o: $(hdrdir)/ruby/internal/encoding/transcode.h -pathname.o: $(hdrdir)/ruby/internal/error.h -pathname.o: $(hdrdir)/ruby/internal/eval.h -pathname.o: $(hdrdir)/ruby/internal/event.h -pathname.o: $(hdrdir)/ruby/internal/fl_type.h -pathname.o: $(hdrdir)/ruby/internal/gc.h -pathname.o: $(hdrdir)/ruby/internal/glob.h -pathname.o: $(hdrdir)/ruby/internal/globals.h -pathname.o: $(hdrdir)/ruby/internal/has/attribute.h -pathname.o: $(hdrdir)/ruby/internal/has/builtin.h -pathname.o: $(hdrdir)/ruby/internal/has/c_attribute.h -pathname.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h -pathname.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h -pathname.o: $(hdrdir)/ruby/internal/has/extension.h -pathname.o: $(hdrdir)/ruby/internal/has/feature.h -pathname.o: $(hdrdir)/ruby/internal/has/warning.h -pathname.o: $(hdrdir)/ruby/internal/intern/array.h -pathname.o: $(hdrdir)/ruby/internal/intern/bignum.h -pathname.o: $(hdrdir)/ruby/internal/intern/class.h -pathname.o: $(hdrdir)/ruby/internal/intern/compar.h -pathname.o: $(hdrdir)/ruby/internal/intern/complex.h -pathname.o: $(hdrdir)/ruby/internal/intern/cont.h -pathname.o: $(hdrdir)/ruby/internal/intern/dir.h -pathname.o: $(hdrdir)/ruby/internal/intern/enum.h -pathname.o: $(hdrdir)/ruby/internal/intern/enumerator.h -pathname.o: $(hdrdir)/ruby/internal/intern/error.h -pathname.o: $(hdrdir)/ruby/internal/intern/eval.h -pathname.o: $(hdrdir)/ruby/internal/intern/file.h -pathname.o: $(hdrdir)/ruby/internal/intern/hash.h -pathname.o: $(hdrdir)/ruby/internal/intern/io.h -pathname.o: $(hdrdir)/ruby/internal/intern/load.h -pathname.o: $(hdrdir)/ruby/internal/intern/marshal.h -pathname.o: $(hdrdir)/ruby/internal/intern/numeric.h -pathname.o: $(hdrdir)/ruby/internal/intern/object.h -pathname.o: $(hdrdir)/ruby/internal/intern/parse.h -pathname.o: $(hdrdir)/ruby/internal/intern/proc.h -pathname.o: $(hdrdir)/ruby/internal/intern/process.h -pathname.o: $(hdrdir)/ruby/internal/intern/random.h -pathname.o: $(hdrdir)/ruby/internal/intern/range.h -pathname.o: $(hdrdir)/ruby/internal/intern/rational.h -pathname.o: $(hdrdir)/ruby/internal/intern/re.h -pathname.o: $(hdrdir)/ruby/internal/intern/ruby.h -pathname.o: $(hdrdir)/ruby/internal/intern/select.h -pathname.o: $(hdrdir)/ruby/internal/intern/select/largesize.h -pathname.o: $(hdrdir)/ruby/internal/intern/signal.h -pathname.o: $(hdrdir)/ruby/internal/intern/sprintf.h -pathname.o: $(hdrdir)/ruby/internal/intern/string.h -pathname.o: $(hdrdir)/ruby/internal/intern/struct.h -pathname.o: $(hdrdir)/ruby/internal/intern/thread.h -pathname.o: $(hdrdir)/ruby/internal/intern/time.h -pathname.o: $(hdrdir)/ruby/internal/intern/variable.h -pathname.o: $(hdrdir)/ruby/internal/intern/vm.h -pathname.o: $(hdrdir)/ruby/internal/interpreter.h -pathname.o: $(hdrdir)/ruby/internal/iterator.h -pathname.o: $(hdrdir)/ruby/internal/memory.h -pathname.o: $(hdrdir)/ruby/internal/method.h -pathname.o: $(hdrdir)/ruby/internal/module.h -pathname.o: $(hdrdir)/ruby/internal/newobj.h -pathname.o: $(hdrdir)/ruby/internal/scan_args.h -pathname.o: $(hdrdir)/ruby/internal/special_consts.h -pathname.o: $(hdrdir)/ruby/internal/static_assert.h -pathname.o: $(hdrdir)/ruby/internal/stdalign.h -pathname.o: $(hdrdir)/ruby/internal/stdbool.h -pathname.o: $(hdrdir)/ruby/internal/symbol.h -pathname.o: $(hdrdir)/ruby/internal/value.h -pathname.o: $(hdrdir)/ruby/internal/value_type.h -pathname.o: $(hdrdir)/ruby/internal/variable.h -pathname.o: $(hdrdir)/ruby/internal/warning_push.h -pathname.o: $(hdrdir)/ruby/internal/xmalloc.h -pathname.o: $(hdrdir)/ruby/missing.h -pathname.o: $(hdrdir)/ruby/onigmo.h -pathname.o: $(hdrdir)/ruby/oniguruma.h -pathname.o: $(hdrdir)/ruby/ruby.h -pathname.o: $(hdrdir)/ruby/st.h -pathname.o: $(hdrdir)/ruby/subst.h -pathname.o: pathname.c -# AUTOGENERATED DEPENDENCIES END diff --git a/ext/pathname/extconf.rb b/ext/pathname/extconf.rb deleted file mode 100644 index 84e68277aa..0000000000 --- a/ext/pathname/extconf.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: false -require 'mkmf' -have_func("rb_file_s_birthtime") -create_makefile('pathname') diff --git a/ext/pathname/lib/pathname.rb b/ext/pathname/lib/pathname.rb deleted file mode 100644 index dc639174d5..0000000000 --- a/ext/pathname/lib/pathname.rb +++ /dev/null @@ -1,605 +0,0 @@ -# frozen_string_literal: true -# -# = pathname.rb -# -# Object-Oriented Pathname Class -# -# Author:: Tanaka Akira <akr@m17n.org> -# Documentation:: Author and Gavin Sinclair -# -# For documentation, see class Pathname. -# - -require 'pathname.so' - -class Pathname - - VERSION = "0.3.0" - - # :stopdoc: - - # to_path is implemented so Pathname objects are usable with File.open, etc. - TO_PATH = :to_path - - SAME_PATHS = if File::FNM_SYSCASE.nonzero? - # Avoid #zero? here because #casecmp can return nil. - proc {|a, b| a.casecmp(b) == 0} - else - proc {|a, b| a == b} - end - - - if File::ALT_SEPARATOR - SEPARATOR_LIST = "#{Regexp.quote File::ALT_SEPARATOR}#{Regexp.quote File::SEPARATOR}" - SEPARATOR_PAT = /[#{SEPARATOR_LIST}]/ - else - SEPARATOR_LIST = "#{Regexp.quote File::SEPARATOR}" - SEPARATOR_PAT = /#{Regexp.quote File::SEPARATOR}/ - end - - if File.dirname('A:') == 'A:.' # DOSish drive letter - ABSOLUTE_PATH = /\A(?:[A-Za-z]:|#{SEPARATOR_PAT})/o - else - ABSOLUTE_PATH = /\A#{SEPARATOR_PAT}/o - end - private_constant :ABSOLUTE_PATH - - # :startdoc: - - # chop_basename(path) -> [pre-basename, basename] or nil - def chop_basename(path) # :nodoc: - base = File.basename(path) - if /\A#{SEPARATOR_PAT}?\z/o.match?(base) - return nil - else - return path[0, path.rindex(base)], base - end - end - private :chop_basename - - # split_names(path) -> prefix, [name, ...] - def split_names(path) # :nodoc: - names = [] - while r = chop_basename(path) - path, basename = r - names.unshift basename - end - return path, names - end - private :split_names - - def prepend_prefix(prefix, relpath) # :nodoc: - if relpath.empty? - File.dirname(prefix) - elsif /#{SEPARATOR_PAT}/o.match?(prefix) - prefix = File.dirname(prefix) - prefix = File.join(prefix, "") if File.basename(prefix + 'a') != 'a' - prefix + relpath - else - prefix + relpath - end - end - private :prepend_prefix - - # Returns clean pathname of +self+ with consecutive slashes and useless dots - # removed. The filesystem is not accessed. - # - # If +consider_symlink+ is +true+, then a more conservative algorithm is used - # to avoid breaking symbolic linkages. This may retain more +..+ - # entries than absolutely necessary, but without accessing the filesystem, - # this can't be avoided. - # - # See Pathname#realpath. - # - def cleanpath(consider_symlink=false) - if consider_symlink - cleanpath_conservative - else - cleanpath_aggressive - end - end - - # - # Clean the path simply by resolving and removing excess +.+ and +..+ entries. - # Nothing more, nothing less. - # - def cleanpath_aggressive # :nodoc: - path = @path - names = [] - pre = path - while r = chop_basename(pre) - pre, base = r - case base - when '.' - when '..' - names.unshift base - else - if names[0] == '..' - names.shift - else - names.unshift base - end - end - end - pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR - if /#{SEPARATOR_PAT}/o.match?(File.basename(pre)) - names.shift while names[0] == '..' - end - self.class.new(prepend_prefix(pre, File.join(*names))) - end - private :cleanpath_aggressive - - # has_trailing_separator?(path) -> bool - def has_trailing_separator?(path) # :nodoc: - if r = chop_basename(path) - pre, basename = r - pre.length + basename.length < path.length - else - false - end - end - private :has_trailing_separator? - - # add_trailing_separator(path) -> path - def add_trailing_separator(path) # :nodoc: - if File.basename(path + 'a') == 'a' - path - else - File.join(path, "") # xxx: Is File.join is appropriate to add separator? - end - end - private :add_trailing_separator - - def del_trailing_separator(path) # :nodoc: - if r = chop_basename(path) - pre, basename = r - pre + basename - elsif /#{SEPARATOR_PAT}+\z/o =~ path - $` + File.dirname(path)[/#{SEPARATOR_PAT}*\z/o] - else - path - end - end - private :del_trailing_separator - - def cleanpath_conservative # :nodoc: - path = @path - names = [] - pre = path - while r = chop_basename(pre) - pre, base = r - names.unshift base if base != '.' - end - pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR - if /#{SEPARATOR_PAT}/o.match?(File.basename(pre)) - names.shift while names[0] == '..' - end - if names.empty? - self.class.new(File.dirname(pre)) - else - if names.last != '..' && File.basename(path) == '.' - names << '.' - end - result = prepend_prefix(pre, File.join(*names)) - if /\A(?:\.|\.\.)\z/ !~ names.last && has_trailing_separator?(path) - self.class.new(add_trailing_separator(result)) - else - self.class.new(result) - end - end - end - private :cleanpath_conservative - - # Returns the parent directory. - # - # This is same as <code>self + '..'</code>. - def parent - self + '..' - end - - # Returns +true+ if +self+ points to a mountpoint. - def mountpoint? - begin - stat1 = self.lstat - stat2 = self.parent.lstat - stat1.dev != stat2.dev || stat1.ino == stat2.ino - rescue Errno::ENOENT - false - end - end - - # - # Predicate method for root directories. Returns +true+ if the - # pathname consists of consecutive slashes. - # - # It doesn't access the filesystem. So it may return +false+ for some - # pathnames which points to roots such as <tt>/usr/..</tt>. - # - def root? - chop_basename(@path) == nil && /#{SEPARATOR_PAT}/o.match?(@path) - end - - # Predicate method for testing whether a path is absolute. - # - # It returns +true+ if the pathname begins with a slash. - # - # p = Pathname.new('/im/sure') - # p.absolute? - # #=> true - # - # p = Pathname.new('not/so/sure') - # p.absolute? - # #=> false - def absolute? - ABSOLUTE_PATH.match? @path - end - - # The opposite of Pathname#absolute? - # - # It returns +false+ if the pathname begins with a slash. - # - # p = Pathname.new('/im/sure') - # p.relative? - # #=> false - # - # p = Pathname.new('not/so/sure') - # p.relative? - # #=> true - def relative? - !absolute? - end - - # - # Iterates over each component of the path. - # - # Pathname.new("/usr/bin/ruby").each_filename {|filename| ... } - # # yields "usr", "bin", and "ruby". - # - # Returns an Enumerator if no block was given. - # - # enum = Pathname.new("/usr/bin/ruby").each_filename - # # ... do stuff ... - # enum.each { |e| ... } - # # yields "usr", "bin", and "ruby". - # - def each_filename # :yield: filename - return to_enum(__method__) unless block_given? - _, names = split_names(@path) - names.each {|filename| yield filename } - nil - end - - # Iterates over and yields a new Pathname object - # for each element in the given path in descending order. - # - # Pathname.new('/path/to/some/file.rb').descend {|v| p v} - # #<Pathname:/> - # #<Pathname:/path> - # #<Pathname:/path/to> - # #<Pathname:/path/to/some> - # #<Pathname:/path/to/some/file.rb> - # - # Pathname.new('path/to/some/file.rb').descend {|v| p v} - # #<Pathname:path> - # #<Pathname:path/to> - # #<Pathname:path/to/some> - # #<Pathname:path/to/some/file.rb> - # - # Returns an Enumerator if no block was given. - # - # enum = Pathname.new("/usr/bin/ruby").descend - # # ... do stuff ... - # enum.each { |e| ... } - # # yields Pathnames /, /usr, /usr/bin, and /usr/bin/ruby. - # - # It doesn't access the filesystem. - # - def descend - return to_enum(__method__) unless block_given? - vs = [] - ascend {|v| vs << v } - vs.reverse_each {|v| yield v } - nil - end - - # Iterates over and yields a new Pathname object - # for each element in the given path in ascending order. - # - # Pathname.new('/path/to/some/file.rb').ascend {|v| p v} - # #<Pathname:/path/to/some/file.rb> - # #<Pathname:/path/to/some> - # #<Pathname:/path/to> - # #<Pathname:/path> - # #<Pathname:/> - # - # Pathname.new('path/to/some/file.rb').ascend {|v| p v} - # #<Pathname:path/to/some/file.rb> - # #<Pathname:path/to/some> - # #<Pathname:path/to> - # #<Pathname:path> - # - # Returns an Enumerator if no block was given. - # - # enum = Pathname.new("/usr/bin/ruby").ascend - # # ... do stuff ... - # enum.each { |e| ... } - # # yields Pathnames /usr/bin/ruby, /usr/bin, /usr, and /. - # - # It doesn't access the filesystem. - # - def ascend - return to_enum(__method__) unless block_given? - path = @path - yield self - while r = chop_basename(path) - path, = r - break if path.empty? - yield self.class.new(del_trailing_separator(path)) - end - end - - # - # Appends a pathname fragment to +self+ to produce a new Pathname object. - # Since +other+ is considered as a path relative to +self+, if +other+ is - # an absolute path, the new Pathname object is created from just +other+. - # - # p1 = Pathname.new("/usr") # Pathname:/usr - # p2 = p1 + "bin/ruby" # Pathname:/usr/bin/ruby - # p3 = p1 + "/etc/passwd" # Pathname:/etc/passwd - # - # # / is aliased to +. - # p4 = p1 / "bin/ruby" # Pathname:/usr/bin/ruby - # p5 = p1 / "/etc/passwd" # Pathname:/etc/passwd - # - # This method doesn't access the file system; it is pure string manipulation. - # - def +(other) - other = Pathname.new(other) unless Pathname === other - Pathname.new(plus(@path, other.to_s)) - end - alias / + - - def plus(path1, path2) # -> path # :nodoc: - prefix2 = path2 - index_list2 = [] - basename_list2 = [] - while r2 = chop_basename(prefix2) - prefix2, basename2 = r2 - index_list2.unshift prefix2.length - basename_list2.unshift basename2 - end - return path2 if prefix2 != '' - prefix1 = path1 - while true - while !basename_list2.empty? && basename_list2.first == '.' - index_list2.shift - basename_list2.shift - end - break unless r1 = chop_basename(prefix1) - prefix1, basename1 = r1 - next if basename1 == '.' - if basename1 == '..' || basename_list2.empty? || basename_list2.first != '..' - prefix1 = prefix1 + basename1 - break - end - index_list2.shift - basename_list2.shift - end - r1 = chop_basename(prefix1) - if !r1 && (r1 = /#{SEPARATOR_PAT}/o.match?(File.basename(prefix1))) - while !basename_list2.empty? && basename_list2.first == '..' - index_list2.shift - basename_list2.shift - end - end - if !basename_list2.empty? - suffix2 = path2[index_list2.first..-1] - r1 ? File.join(prefix1, suffix2) : prefix1 + suffix2 - else - r1 ? prefix1 : File.dirname(prefix1) - end - end - private :plus - - # - # Joins the given pathnames onto +self+ to create a new Pathname object. - # This is effectively the same as using Pathname#+ to append +self+ and - # all arguments sequentially. - # - # path0 = Pathname.new("/usr") # Pathname:/usr - # path0 = path0.join("bin/ruby") # Pathname:/usr/bin/ruby - # # is the same as - # path1 = Pathname.new("/usr") + "bin/ruby" # Pathname:/usr/bin/ruby - # path0 == path1 - # #=> true - # - def join(*args) - return self if args.empty? - result = args.pop - result = Pathname.new(result) unless Pathname === result - return result if result.absolute? - args.reverse_each {|arg| - arg = Pathname.new(arg) unless Pathname === arg - result = arg + result - return result if result.absolute? - } - self + result - end - - # - # Returns the children of the directory (files and subdirectories, not - # recursive) as an array of Pathname objects. - # - # By default, the returned pathnames will have enough information to access - # the files. If you set +with_directory+ to +false+, then the returned - # pathnames will contain the filename only. - # - # For example: - # pn = Pathname("/usr/lib/ruby/1.8") - # pn.children - # # -> [ Pathname:/usr/lib/ruby/1.8/English.rb, - # Pathname:/usr/lib/ruby/1.8/Env.rb, - # Pathname:/usr/lib/ruby/1.8/abbrev.rb, ... ] - # pn.children(false) - # # -> [ Pathname:English.rb, Pathname:Env.rb, Pathname:abbrev.rb, ... ] - # - # Note that the results never contain the entries +.+ and +..+ in - # the directory because they are not children. - # - def children(with_directory=true) - with_directory = false if @path == '.' - result = [] - Dir.foreach(@path) {|e| - next if e == '.' || e == '..' - if with_directory - result << self.class.new(File.join(@path, e)) - else - result << self.class.new(e) - end - } - result - end - - # Iterates over the children of the directory - # (files and subdirectories, not recursive). - # - # It yields Pathname object for each child. - # - # By default, the yielded pathnames will have enough information to access - # the files. - # - # If you set +with_directory+ to +false+, then the returned pathnames will - # contain the filename only. - # - # Pathname("/usr/local").each_child {|f| p f } - # #=> #<Pathname:/usr/local/share> - # # #<Pathname:/usr/local/bin> - # # #<Pathname:/usr/local/games> - # # #<Pathname:/usr/local/lib> - # # #<Pathname:/usr/local/include> - # # #<Pathname:/usr/local/sbin> - # # #<Pathname:/usr/local/src> - # # #<Pathname:/usr/local/man> - # - # Pathname("/usr/local").each_child(false) {|f| p f } - # #=> #<Pathname:share> - # # #<Pathname:bin> - # # #<Pathname:games> - # # #<Pathname:lib> - # # #<Pathname:include> - # # #<Pathname:sbin> - # # #<Pathname:src> - # # #<Pathname:man> - # - # Note that the results never contain the entries +.+ and +..+ in - # the directory because they are not children. - # - # See Pathname#children - # - def each_child(with_directory=true, &b) - children(with_directory).each(&b) - end - - # - # Returns a relative path from the given +base_directory+ to the receiver. - # - # If +self+ is absolute, then +base_directory+ must be absolute too. - # - # If +self+ is relative, then +base_directory+ must be relative too. - # - # This method doesn't access the filesystem. It assumes no symlinks. - # - # ArgumentError is raised when it cannot find a relative path. - # - # Note that this method does not handle situations where the case sensitivity - # of the filesystem in use differs from the operating system default. - # - def relative_path_from(base_directory) - base_directory = Pathname.new(base_directory) unless base_directory.is_a? Pathname - dest_directory = self.cleanpath.to_s - base_directory = base_directory.cleanpath.to_s - dest_prefix = dest_directory - dest_names = [] - while r = chop_basename(dest_prefix) - dest_prefix, basename = r - dest_names.unshift basename if basename != '.' - end - base_prefix = base_directory - base_names = [] - while r = chop_basename(base_prefix) - base_prefix, basename = r - base_names.unshift basename if basename != '.' - end - unless SAME_PATHS[dest_prefix, base_prefix] - raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}" - end - while !dest_names.empty? && - !base_names.empty? && - SAME_PATHS[dest_names.first, base_names.first] - dest_names.shift - base_names.shift - end - if base_names.include? '..' - raise ArgumentError, "base_directory has ..: #{base_directory.inspect}" - end - base_names.fill('..') - relpath_names = base_names + dest_names - if relpath_names.empty? - Pathname.new('.') - else - Pathname.new(File.join(*relpath_names)) - end - end -end - - -class Pathname # * Find * - # - # Iterates over the directory tree in a depth first manner, yielding a - # Pathname for each file under "this" directory. - # - # Returns an Enumerator if no block is given. - # - # Since it is implemented by the standard library module Find, Find.prune can - # be used to control the traversal. - # - # If +self+ is +.+, yielded pathnames begin with a filename in the - # current directory, not +./+. - # - # See Find.find - # - def find(ignore_error: true) # :yield: pathname - return to_enum(__method__, ignore_error: ignore_error) unless block_given? - require 'find' - if @path == '.' - Find.find(@path, ignore_error: ignore_error) {|f| yield self.class.new(f.sub(%r{\A\./}, '')) } - else - Find.find(@path, ignore_error: ignore_error) {|f| yield self.class.new(f) } - end - end -end - - -class Pathname # * FileUtils * - # Creates a full path, including any intermediate directories that don't yet - # exist. - # - # See FileUtils.mkpath and FileUtils.mkdir_p - def mkpath(mode: nil) - require 'fileutils' - FileUtils.mkpath(@path, mode: mode) - nil - end - - # Recursively deletes a directory, including all directories beneath it. - # - # See FileUtils.rm_rf - def rmtree(noop: nil, verbose: nil, secure: nil) - # The name "rmtree" is borrowed from File::Path of Perl. - # File::Path provides "mkpath" and "rmtree". - require 'fileutils' - FileUtils.rm_rf(@path, noop: noop, verbose: verbose, secure: secure) - nil - end -end - diff --git a/ext/pathname/pathname.c b/ext/pathname/pathname.c deleted file mode 100644 index 878f216fb5..0000000000 --- a/ext/pathname/pathname.c +++ /dev/null @@ -1,1673 +0,0 @@ -#include "ruby.h" -#include "ruby/encoding.h" - -static VALUE rb_cPathname; -static ID id_ENOTDIR; -static ID id_at_path; -static ID id_atime; -static ID id_base; -static ID id_basename; -static ID id_binread; -static ID id_binwrite; -static ID id_birthtime; -static ID id_blockdev_p; -static ID id_chardev_p; -static ID id_chmod; -static ID id_chown; -static ID id_ctime; -static ID id_directory_p; -static ID id_dirname; -static ID id_empty_p; -static ID id_entries; -static ID id_executable_p; -static ID id_executable_real_p; -static ID id_exist_p; -static ID id_expand_path; -static ID id_extname; -static ID id_file_p; -static ID id_fnmatch; -static ID id_foreach; -static ID id_ftype; -static ID id_getwd; -static ID id_glob; -static ID id_grpowned_p; -static ID id_lchmod; -static ID id_lchown; -static ID id_link; -static ID id_lstat; -static ID id_lutime; -static ID id_mkdir; -static ID id_mtime; -static ID id_open; -static ID id_owned_p; -static ID id_pipe_p; -static ID id_read; -static ID id_readable_p; -static ID id_readable_real_p; -static ID id_readlines; -static ID id_readlink; -static ID id_realdirpath; -static ID id_realpath; -static ID id_rename; -static ID id_rmdir; -static ID id_setgid_p; -static ID id_setuid_p; -static ID id_size; -static ID id_size_p; -static ID id_socket_p; -static ID id_split; -static ID id_stat; -static ID id_sticky_p; -static ID id_sub; -static ID id_symlink; -static ID id_symlink_p; -static ID id_sysopen; -static ID id_to_path; -static ID id_truncate; -static ID id_unlink; -static ID id_utime; -static ID id_world_readable_p; -static ID id_world_writable_p; -static ID id_writable_p; -static ID id_writable_real_p; -static ID id_write; -static ID id_zero_p; - -static VALUE -get_strpath(VALUE obj) -{ - VALUE strpath; - strpath = rb_ivar_get(obj, id_at_path); - if (!RB_TYPE_P(strpath, T_STRING)) - rb_raise(rb_eTypeError, "unexpected @path"); - return strpath; -} - -static void -set_strpath(VALUE obj, VALUE val) -{ - rb_ivar_set(obj, id_at_path, val); -} - -/* - * Create a Pathname object from the given String (or String-like object). - * If +path+ contains a NULL character (<tt>\0</tt>), an ArgumentError is raised. - */ -static VALUE -path_initialize(VALUE self, VALUE arg) -{ - VALUE str; - if (RB_TYPE_P(arg, T_STRING)) { - str = arg; - } - else { - str = rb_check_funcall(arg, id_to_path, 0, NULL); - if (str == Qundef) - str = arg; - StringValue(str); - } - if (memchr(RSTRING_PTR(str), '\0', RSTRING_LEN(str))) - rb_raise(rb_eArgError, "pathname contains null byte"); - str = rb_obj_dup(str); - - set_strpath(self, str); - return self; -} - -/* - * call-seq: - * pathname.freeze -> obj - * - * Freezes this Pathname. - * - * See Object.freeze. - */ -static VALUE -path_freeze(VALUE self) -{ - rb_call_super(0, 0); - rb_str_freeze(get_strpath(self)); - return self; -} - -/* - * Compare this pathname with +other+. The comparison is string-based. - * Be aware that two different paths (<tt>foo.txt</tt> and <tt>./foo.txt</tt>) - * can refer to the same file. - */ -static VALUE -path_eq(VALUE self, VALUE other) -{ - if (!rb_obj_is_kind_of(other, rb_cPathname)) - return Qfalse; - return rb_str_equal(get_strpath(self), get_strpath(other)); -} - -/* - * Provides a case-sensitive comparison operator for pathnames. - * - * Pathname.new('/usr') <=> Pathname.new('/usr/bin') - * #=> -1 - * Pathname.new('/usr/bin') <=> Pathname.new('/usr/bin') - * #=> 0 - * Pathname.new('/usr/bin') <=> Pathname.new('/USR/BIN') - * #=> 1 - * - * It will return +-1+, +0+ or +1+ depending on the value of the left argument - * relative to the right argument. Or it will return +nil+ if the arguments - * are not comparable. - */ -static VALUE -path_cmp(VALUE self, VALUE other) -{ - VALUE s1, s2; - char *p1, *p2; - char *e1, *e2; - if (!rb_obj_is_kind_of(other, rb_cPathname)) - return Qnil; - s1 = get_strpath(self); - s2 = get_strpath(other); - p1 = RSTRING_PTR(s1); - p2 = RSTRING_PTR(s2); - e1 = p1 + RSTRING_LEN(s1); - e2 = p2 + RSTRING_LEN(s2); - while (p1 < e1 && p2 < e2) { - int c1, c2; - c1 = (unsigned char)*p1++; - c2 = (unsigned char)*p2++; - if (c1 == '/') c1 = '\0'; - if (c2 == '/') c2 = '\0'; - if (c1 != c2) { - if (c1 < c2) - return INT2FIX(-1); - else - return INT2FIX(1); - } - } - if (p1 < e1) - return INT2FIX(1); - if (p2 < e2) - return INT2FIX(-1); - return INT2FIX(0); -} - -#ifndef ST2FIX -#define ST2FIX(h) LONG2FIX((long)(h)) -#endif - -/* :nodoc: */ -static VALUE -path_hash(VALUE self) -{ - return ST2FIX(rb_str_hash(get_strpath(self))); -} - -/* - * call-seq: - * pathname.to_s -> string - * pathname.to_path -> string - * - * Return the path as a String. - * - * to_path is implemented so Pathname objects are usable with File.open, etc. - */ -static VALUE -path_to_s(VALUE self) -{ - return rb_obj_dup(get_strpath(self)); -} - -/* :nodoc: */ -static VALUE -path_inspect(VALUE self) -{ - const char *c = rb_obj_classname(self); - VALUE str = get_strpath(self); - return rb_sprintf("#<%s:%"PRIsVALUE">", c, str); -} - -/* - * Return a pathname which is substituted by String#sub. - * - * path1 = Pathname.new('/usr/bin/perl') - * path1.sub('perl', 'ruby') - * #=> #<Pathname:/usr/bin/ruby> - */ -static VALUE -path_sub(int argc, VALUE *argv, VALUE self) -{ - VALUE str = get_strpath(self); - - if (rb_block_given_p()) { - str = rb_block_call(str, id_sub, argc, argv, 0, 0); - } - else { - str = rb_funcallv(str, id_sub, argc, argv); - } - return rb_class_new_instance(1, &str, rb_obj_class(self)); -} - -/* - * Return a pathname with +repl+ added as a suffix to the basename. - * - * If self has no extension part, +repl+ is appended. - * - * Pathname.new('/usr/bin/shutdown').sub_ext('.rb') - * #=> #<Pathname:/usr/bin/shutdown.rb> - */ -static VALUE -path_sub_ext(VALUE self, VALUE repl) -{ - VALUE str = get_strpath(self); - VALUE str2; - long extlen; - const char *ext; - const char *p; - - StringValue(repl); - p = RSTRING_PTR(str); - extlen = RSTRING_LEN(str); - ext = ruby_enc_find_extname(p, &extlen, rb_enc_get(str)); - if (ext == NULL) { - ext = p + RSTRING_LEN(str); - } - else if (extlen <= 1) { - ext += extlen; - } - str2 = rb_str_subseq(str, 0, ext-p); - rb_str_append(str2, repl); - return rb_class_new_instance(1, &str2, rb_obj_class(self)); -} - -/* Facade for File */ - -/* - * Returns the real (absolute) pathname for +self+ in the actual - * filesystem. - * - * Does not contain symlinks or useless dots, +..+ and +.+. - * - * All components of the pathname must exist when this method is - * called. - * - */ -static VALUE -path_realpath(int argc, VALUE *argv, VALUE self) -{ - VALUE basedir, str; - rb_scan_args(argc, argv, "01", &basedir); - str = rb_funcall(rb_cFile, id_realpath, 2, get_strpath(self), basedir); - return rb_class_new_instance(1, &str, rb_obj_class(self)); -} - -/* - * Returns the real (absolute) pathname of +self+ in the actual filesystem. - * - * Does not contain symlinks or useless dots, +..+ and +.+. - * - * The last component of the real pathname can be nonexistent. - */ -static VALUE -path_realdirpath(int argc, VALUE *argv, VALUE self) -{ - VALUE basedir, str; - rb_scan_args(argc, argv, "01", &basedir); - str = rb_funcall(rb_cFile, id_realdirpath, 2, get_strpath(self), basedir); - return rb_class_new_instance(1, &str, rb_obj_class(self)); -} - -/* - * call-seq: - * pathname.each_line {|line| ... } - * pathname.each_line(sep=$/ [, open_args]) {|line| block } -> nil - * pathname.each_line(limit [, open_args]) {|line| block } -> nil - * pathname.each_line(sep, limit [, open_args]) {|line| block } -> nil - * pathname.each_line(...) -> an_enumerator - * - * Iterates over each line in the file and yields a String object for each. - */ -static VALUE -path_each_line(int argc, VALUE *argv, VALUE self) -{ - VALUE args[4]; - int n; - - args[0] = get_strpath(self); - n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]); - if (rb_block_given_p()) { - return rb_block_call_kw(rb_cFile, id_foreach, 1+n, args, 0, 0, RB_PASS_CALLED_KEYWORDS); - } - else { - return rb_funcallv_kw(rb_cFile, id_foreach, 1+n, args, RB_PASS_CALLED_KEYWORDS); - } -} - -/* - * call-seq: - * pathname.read([length [, offset]]) -> string - * pathname.read([length [, offset]], open_args) -> string - * - * Returns all data from the file, or the first +N+ bytes if specified. - * - * See File.read. - * - */ -static VALUE -path_read(int argc, VALUE *argv, VALUE self) -{ - VALUE args[4]; - int n; - - args[0] = get_strpath(self); - n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]); - return rb_funcallv_kw(rb_cFile, id_read, 1+n, args, RB_PASS_CALLED_KEYWORDS); -} - -/* - * call-seq: - * pathname.binread([length [, offset]]) -> string - * - * Returns all the bytes from the file, or the first +N+ if specified. - * - * See File.binread. - * - */ -static VALUE -path_binread(int argc, VALUE *argv, VALUE self) -{ - VALUE args[3]; - int n; - - args[0] = get_strpath(self); - n = rb_scan_args(argc, argv, "02", &args[1], &args[2]); - return rb_funcallv(rb_cFile, id_binread, 1+n, args); -} - -/* - * call-seq: - * pathname.write(string, [offset] ) => fixnum - * pathname.write(string, [offset], open_args ) => fixnum - * - * Writes +contents+ to the file. - * - * See File.write. - * - */ -static VALUE -path_write(int argc, VALUE *argv, VALUE self) -{ - VALUE args[4]; - int n; - - args[0] = get_strpath(self); - n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]); - return rb_funcallv_kw(rb_cFile, id_write, 1+n, args, RB_PASS_CALLED_KEYWORDS); -} - -/* - * call-seq: - * pathname.binwrite(string, [offset] ) => fixnum - * pathname.binwrite(string, [offset], open_args ) => fixnum - * - * Writes +contents+ to the file, opening it in binary mode. - * - * See File.binwrite. - * - */ -static VALUE -path_binwrite(int argc, VALUE *argv, VALUE self) -{ - VALUE args[4]; - int n; - - args[0] = get_strpath(self); - n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]); - return rb_funcallv_kw(rb_cFile, id_binwrite, 1+n, args, RB_PASS_CALLED_KEYWORDS); -} - -/* - * call-seq: - * pathname.readlines(sep=$/ [, open_args]) -> array - * pathname.readlines(limit [, open_args]) -> array - * pathname.readlines(sep, limit [, open_args]) -> array - * - * Returns all the lines from the file. - * - * See File.readlines. - * - */ -static VALUE -path_readlines(int argc, VALUE *argv, VALUE self) -{ - VALUE args[4]; - int n; - - args[0] = get_strpath(self); - n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]); - return rb_funcallv_kw(rb_cFile, id_readlines, 1+n, args, RB_PASS_CALLED_KEYWORDS); -} - -/* - * call-seq: - * pathname.sysopen([mode, [perm]]) -> fixnum - * - * See IO.sysopen. - * - */ -static VALUE -path_sysopen(int argc, VALUE *argv, VALUE self) -{ - VALUE args[3]; - int n; - - args[0] = get_strpath(self); - n = rb_scan_args(argc, argv, "02", &args[1], &args[2]); - return rb_funcallv(rb_cIO, id_sysopen, 1+n, args); -} - -/* - * call-seq: - * pathname.atime -> time - * - * Returns the last access time for the file. - * - * See File.atime. - */ -static VALUE -path_atime(VALUE self) -{ - return rb_funcall(rb_cFile, id_atime, 1, get_strpath(self)); -} - -#if defined(HAVE_RB_FILE_S_BIRTHTIME) -/* - * call-seq: - * pathname.birthtime -> time - * - * Returns the birth time for the file. - * If the platform doesn't have birthtime, raises NotImplementedError. - * - * See File.birthtime. - */ -static VALUE -path_birthtime(VALUE self) -{ - return rb_funcall(rb_cFile, id_birthtime, 1, get_strpath(self)); -} -#else -/* check at compilation time for `respond_to?` */ -# define path_birthtime rb_f_notimplement -#endif - -/* - * call-seq: - * pathname.ctime -> time - * - * Returns the last change time, using directory information, not the file itself. - * - * See File.ctime. - */ -static VALUE -path_ctime(VALUE self) -{ - return rb_funcall(rb_cFile, id_ctime, 1, get_strpath(self)); -} - -/* - * call-seq: - * pathname.mtime -> time - * - * Returns the last modified time of the file. - * - * See File.mtime. - */ -static VALUE -path_mtime(VALUE self) -{ - return rb_funcall(rb_cFile, id_mtime, 1, get_strpath(self)); -} - -/* - * call-seq: - * pathname.chmod(mode_int) -> integer - * - * Changes file permissions. - * - * See File.chmod. - */ -static VALUE -path_chmod(VALUE self, VALUE mode) -{ - return rb_funcall(rb_cFile, id_chmod, 2, mode, get_strpath(self)); -} - -/* - * call-seq: - * pathname.lchmod(mode_int) -> integer - * - * Same as Pathname.chmod, but does not follow symbolic links. - * - * See File.lchmod. - */ -static VALUE -path_lchmod(VALUE self, VALUE mode) -{ - return rb_funcall(rb_cFile, id_lchmod, 2, mode, get_strpath(self)); -} - -/* - * call-seq: - * pathname.chown(owner_int, group_int) -> integer - * - * Change owner and group of the file. - * - * See File.chown. - */ -static VALUE -path_chown(VALUE self, VALUE owner, VALUE group) -{ - return rb_funcall(rb_cFile, id_chown, 3, owner, group, get_strpath(self)); -} - -/* - * call-seq: - * pathname.lchown(owner_int, group_int) -> integer - * - * Same as Pathname.chown, but does not follow symbolic links. - * - * See File.lchown. - */ -static VALUE -path_lchown(VALUE self, VALUE owner, VALUE group) -{ - return rb_funcall(rb_cFile, id_lchown, 3, owner, group, get_strpath(self)); -} - -/* - * call-seq: - * pathname.fnmatch(pattern, [flags]) -> true or false - * pathname.fnmatch?(pattern, [flags]) -> true or false - * - * Return +true+ if the receiver matches the given pattern. - * - * See File.fnmatch. - */ -static VALUE -path_fnmatch(int argc, VALUE *argv, VALUE self) -{ - VALUE str = get_strpath(self); - VALUE pattern, flags; - if (rb_scan_args(argc, argv, "11", &pattern, &flags) == 1) - return rb_funcall(rb_cFile, id_fnmatch, 2, pattern, str); - else - return rb_funcall(rb_cFile, id_fnmatch, 3, pattern, str, flags); -} - -/* - * call-seq: - * pathname.ftype -> string - * - * Returns "type" of file ("file", "directory", etc). - * - * See File.ftype. - */ -static VALUE -path_ftype(VALUE self) -{ - return rb_funcall(rb_cFile, id_ftype, 1, get_strpath(self)); -} - -/* - * call-seq: - * pathname.make_link(old) - * - * Creates a hard link at _pathname_. - * - * See File.link. - */ -static VALUE -path_make_link(VALUE self, VALUE old) -{ - return rb_funcall(rb_cFile, id_link, 2, old, get_strpath(self)); -} - -/* - * call-seq: - * pathname.open() - * pathname.open(mode="r" [, opt]) -> file - * pathname.open([mode [, perm]] [, opt]) -> file - * pathname.open(mode="r" [, opt]) {|file| block } -> obj - * pathname.open([mode [, perm]] [, opt]) {|file| block } -> obj - * - * Opens the file for reading or writing. - * - * See File.open. - */ -static VALUE -path_open(int argc, VALUE *argv, VALUE self) -{ - VALUE args[4]; - int n; - - args[0] = get_strpath(self); - n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]); - if (rb_block_given_p()) { - return rb_block_call_kw(rb_cFile, id_open, 1+n, args, 0, 0, RB_PASS_CALLED_KEYWORDS); - } - else { - return rb_funcallv_kw(rb_cFile, id_open, 1+n, args, RB_PASS_CALLED_KEYWORDS); - } -} - -/* - * Read symbolic link. - * - * See File.readlink. - */ -static VALUE -path_readlink(VALUE self) -{ - VALUE str; - str = rb_funcall(rb_cFile, id_readlink, 1, get_strpath(self)); - return rb_class_new_instance(1, &str, rb_obj_class(self)); -} - -/* - * Rename the file. - * - * See File.rename. - */ -static VALUE -path_rename(VALUE self, VALUE to) -{ - return rb_funcall(rb_cFile, id_rename, 2, get_strpath(self), to); -} - -/* - * Returns a File::Stat object. - * - * See File.stat. - */ -static VALUE -path_stat(VALUE self) -{ - return rb_funcall(rb_cFile, id_stat, 1, get_strpath(self)); -} - -/* - * See File.lstat. - */ -static VALUE -path_lstat(VALUE self) -{ - return rb_funcall(rb_cFile, id_lstat, 1, get_strpath(self)); -} - -/* - * call-seq: - * pathname.make_symlink(old) - * - * Creates a symbolic link. - * - * See File.symlink. - */ -static VALUE -path_make_symlink(VALUE self, VALUE old) -{ - return rb_funcall(rb_cFile, id_symlink, 2, old, get_strpath(self)); -} - -/* - * Truncates the file to +length+ bytes. - * - * See File.truncate. - */ -static VALUE -path_truncate(VALUE self, VALUE length) -{ - return rb_funcall(rb_cFile, id_truncate, 2, get_strpath(self), length); -} - -/* - * Update the access and modification times of the file. - * - * See File.utime. - */ -static VALUE -path_utime(VALUE self, VALUE atime, VALUE mtime) -{ - return rb_funcall(rb_cFile, id_utime, 3, atime, mtime, get_strpath(self)); -} - -/* - * Update the access and modification times of the file. - * - * Same as Pathname#utime, but does not follow symbolic links. - * - * See File.lutime. - */ -static VALUE -path_lutime(VALUE self, VALUE atime, VALUE mtime) -{ - return rb_funcall(rb_cFile, id_lutime, 3, atime, mtime, get_strpath(self)); -} - -/* - * Returns the last component of the path. - * - * See File.basename. - */ -static VALUE -path_basename(int argc, VALUE *argv, VALUE self) -{ - VALUE str = get_strpath(self); - VALUE fext; - if (rb_scan_args(argc, argv, "01", &fext) == 0) - str = rb_funcall(rb_cFile, id_basename, 1, str); - else - str = rb_funcall(rb_cFile, id_basename, 2, str, fext); - return rb_class_new_instance(1, &str, rb_obj_class(self)); -} - -/* - * Returns all but the last component of the path. - * - * See File.dirname. - */ -static VALUE -path_dirname(VALUE self) -{ - VALUE str = get_strpath(self); - str = rb_funcall(rb_cFile, id_dirname, 1, str); - return rb_class_new_instance(1, &str, rb_obj_class(self)); -} - -/* - * Returns the file's extension. - * - * See File.extname. - */ -static VALUE -path_extname(VALUE self) -{ - VALUE str = get_strpath(self); - return rb_funcall(rb_cFile, id_extname, 1, str); -} - -/* - * Returns the absolute path for the file. - * - * See File.expand_path. - */ -static VALUE -path_expand_path(int argc, VALUE *argv, VALUE self) -{ - VALUE str = get_strpath(self); - VALUE dname; - if (rb_scan_args(argc, argv, "01", &dname) == 0) - str = rb_funcall(rb_cFile, id_expand_path, 1, str); - else - str = rb_funcall(rb_cFile, id_expand_path, 2, str, dname); - return rb_class_new_instance(1, &str, rb_obj_class(self)); -} - -/* - * Returns the #dirname and the #basename in an Array. - * - * See File.split. - */ -static VALUE -path_split(VALUE self) -{ - VALUE str = get_strpath(self); - VALUE ary, dirname, basename; - ary = rb_funcall(rb_cFile, id_split, 1, str); - Check_Type(ary, T_ARRAY); - dirname = rb_ary_entry(ary, 0); - basename = rb_ary_entry(ary, 1); - dirname = rb_class_new_instance(1, &dirname, rb_obj_class(self)); - basename = rb_class_new_instance(1, &basename, rb_obj_class(self)); - return rb_ary_new3(2, dirname, basename); -} - -/* - * See FileTest.blockdev?. - */ -static VALUE -path_blockdev_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_blockdev_p, 1, get_strpath(self)); -} - -/* - * See FileTest.chardev?. - */ -static VALUE -path_chardev_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_chardev_p, 1, get_strpath(self)); -} - -/* - * See FileTest.executable?. - */ -static VALUE -path_executable_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_executable_p, 1, get_strpath(self)); -} - -/* - * See FileTest.executable_real?. - */ -static VALUE -path_executable_real_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_executable_real_p, 1, get_strpath(self)); -} - -/* - * See FileTest.exist?. - */ -static VALUE -path_exist_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_exist_p, 1, get_strpath(self)); -} - -/* - * See FileTest.grpowned?. - */ -static VALUE -path_grpowned_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_grpowned_p, 1, get_strpath(self)); -} - -/* - * See FileTest.directory?. - */ -static VALUE -path_directory_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_directory_p, 1, get_strpath(self)); -} - -/* - * See FileTest.file?. - */ -static VALUE -path_file_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_file_p, 1, get_strpath(self)); -} - -/* - * See FileTest.pipe?. - */ -static VALUE -path_pipe_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_pipe_p, 1, get_strpath(self)); -} - -/* - * See FileTest.socket?. - */ -static VALUE -path_socket_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_socket_p, 1, get_strpath(self)); -} - -/* - * See FileTest.owned?. - */ -static VALUE -path_owned_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_owned_p, 1, get_strpath(self)); -} - -/* - * See FileTest.readable?. - */ -static VALUE -path_readable_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_readable_p, 1, get_strpath(self)); -} - -/* - * See FileTest.world_readable?. - */ -static VALUE -path_world_readable_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_world_readable_p, 1, get_strpath(self)); -} - -/* - * See FileTest.readable_real?. - */ -static VALUE -path_readable_real_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_readable_real_p, 1, get_strpath(self)); -} - -/* - * See FileTest.setuid?. - */ -static VALUE -path_setuid_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_setuid_p, 1, get_strpath(self)); -} - -/* - * See FileTest.setgid?. - */ -static VALUE -path_setgid_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_setgid_p, 1, get_strpath(self)); -} - -/* - * See FileTest.size. - */ -static VALUE -path_size(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_size, 1, get_strpath(self)); -} - -/* - * See FileTest.size?. - */ -static VALUE -path_size_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_size_p, 1, get_strpath(self)); -} - -/* - * See FileTest.sticky?. - */ -static VALUE -path_sticky_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_sticky_p, 1, get_strpath(self)); -} - -/* - * See FileTest.symlink?. - */ -static VALUE -path_symlink_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_symlink_p, 1, get_strpath(self)); -} - -/* - * See FileTest.writable?. - */ -static VALUE -path_writable_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_writable_p, 1, get_strpath(self)); -} - -/* - * See FileTest.world_writable?. - */ -static VALUE -path_world_writable_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_world_writable_p, 1, get_strpath(self)); -} - -/* - * See FileTest.writable_real?. - */ -static VALUE -path_writable_real_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_writable_real_p, 1, get_strpath(self)); -} - -/* - * See FileTest.zero?. - */ -static VALUE -path_zero_p(VALUE self) -{ - return rb_funcall(rb_mFileTest, id_zero_p, 1, get_strpath(self)); -} - -/* - * Tests the file is empty. - * - * See Dir#empty? and FileTest.empty?. - */ -static VALUE -path_empty_p(VALUE self) -{ - - VALUE path = get_strpath(self); - if (RTEST(rb_funcall(rb_mFileTest, id_directory_p, 1, path))) - return rb_funcall(rb_cDir, id_empty_p, 1, path); - else - return rb_funcall(rb_mFileTest, id_empty_p, 1, path); -} - -static VALUE -s_glob_i(RB_BLOCK_CALL_FUNC_ARGLIST(elt, klass)) -{ - return rb_yield(rb_class_new_instance(1, &elt, klass)); -} - -/* - * Returns or yields Pathname objects. - * - * Pathname.glob("lib/i*.rb") - * #=> [#<Pathname:lib/ipaddr.rb>, #<Pathname:lib/irb.rb>] - * - * See Dir.glob. - */ -static VALUE -path_s_glob(int argc, VALUE *argv, VALUE klass) -{ - VALUE args[3]; - int n; - - n = rb_scan_args(argc, argv, "12", &args[0], &args[1], &args[2]); - if (rb_block_given_p()) { - return rb_block_call_kw(rb_cDir, id_glob, n, args, s_glob_i, klass, RB_PASS_CALLED_KEYWORDS); - } - else { - VALUE ary; - long i; - ary = rb_funcallv_kw(rb_cDir, id_glob, n, args, RB_PASS_CALLED_KEYWORDS); - ary = rb_convert_type(ary, T_ARRAY, "Array", "to_ary"); - for (i = 0; i < RARRAY_LEN(ary); i++) { - VALUE elt = RARRAY_AREF(ary, i); - elt = rb_class_new_instance(1, &elt, klass); - rb_ary_store(ary, i, elt); - } - return ary; - } -} - -static VALUE -glob_i(RB_BLOCK_CALL_FUNC_ARGLIST(elt, self)) -{ - elt = rb_funcall(self, '+', 1, elt); - return rb_yield(elt); -} - -/* - * Returns or yields Pathname objects. - * - * Pathname("ruby-2.4.2").glob("R*.md") - * #=> [#<Pathname:ruby-2.4.2/README.md>, #<Pathname:ruby-2.4.2/README.ja.md>] - * - * See Dir.glob. - * This method uses the +base+ keyword argument of Dir.glob. - */ -static VALUE -path_glob(int argc, VALUE *argv, VALUE self) -{ - VALUE args[3]; - int n; - - n = rb_scan_args(argc, argv, "11", &args[0], &args[1]); - if (n == 1) - args[1] = INT2FIX(0); - - args[2] = rb_hash_new(); - rb_hash_aset(args[2], ID2SYM(id_base), get_strpath(self)); - - n = 3; - - if (rb_block_given_p()) { - return rb_block_call_kw(rb_cDir, id_glob, n, args, glob_i, self, RB_PASS_KEYWORDS); - } - else { - VALUE ary; - long i; - ary = rb_funcallv_kw(rb_cDir, id_glob, n, args, RB_PASS_KEYWORDS); - ary = rb_convert_type(ary, T_ARRAY, "Array", "to_ary"); - for (i = 0; i < RARRAY_LEN(ary); i++) { - VALUE elt = RARRAY_AREF(ary, i); - elt = rb_funcall(self, '+', 1, elt); - rb_ary_store(ary, i, elt); - } - return ary; - } -} - -/* - * Returns the current working directory as a Pathname. - * - * Pathname.getwd - * #=> #<Pathname:/home/zzak/projects/ruby> - * - * See Dir.getwd. - */ -static VALUE -path_s_getwd(VALUE klass) -{ - VALUE str; - str = rb_funcall(rb_cDir, id_getwd, 0); - return rb_class_new_instance(1, &str, klass); -} - -/* - * Return the entries (files and subdirectories) in the directory, each as a - * Pathname object. - * - * The results contains just the names in the directory, without any trailing - * slashes or recursive look-up. - * - * pp Pathname.new('/usr/local').entries - * #=> [#<Pathname:share>, - * # #<Pathname:lib>, - * # #<Pathname:..>, - * # #<Pathname:include>, - * # #<Pathname:etc>, - * # #<Pathname:bin>, - * # #<Pathname:man>, - * # #<Pathname:games>, - * # #<Pathname:.>, - * # #<Pathname:sbin>, - * # #<Pathname:src>] - * - * The result may contain the current directory <code>#<Pathname:.></code> and - * the parent directory <code>#<Pathname:..></code>. - * - * If you don't want +.+ and +..+ and - * want directories, consider Pathname#children. - */ -static VALUE -path_entries(VALUE self) -{ - VALUE klass, str, ary; - long i; - klass = rb_obj_class(self); - str = get_strpath(self); - ary = rb_funcall(rb_cDir, id_entries, 1, str); - ary = rb_convert_type(ary, T_ARRAY, "Array", "to_ary"); - for (i = 0; i < RARRAY_LEN(ary); i++) { - VALUE elt = RARRAY_AREF(ary, i); - elt = rb_class_new_instance(1, &elt, klass); - rb_ary_store(ary, i, elt); - } - return ary; -} - -/* - * Create the referenced directory. - * - * See Dir.mkdir. - */ -static VALUE -path_mkdir(int argc, VALUE *argv, VALUE self) -{ - VALUE str = get_strpath(self); - VALUE vmode; - if (rb_scan_args(argc, argv, "01", &vmode) == 0) - return rb_funcall(rb_cDir, id_mkdir, 1, str); - else - return rb_funcall(rb_cDir, id_mkdir, 2, str, vmode); -} - -/* - * Remove the referenced directory. - * - * See Dir.rmdir. - */ -static VALUE -path_rmdir(VALUE self) -{ - return rb_funcall(rb_cDir, id_rmdir, 1, get_strpath(self)); -} - -/* - * Opens the referenced directory. - * - * See Dir.open. - */ -static VALUE -path_opendir(VALUE self) -{ - VALUE args[1]; - - args[0] = get_strpath(self); - return rb_block_call(rb_cDir, id_open, 1, args, 0, 0); -} - -static VALUE -each_entry_i(RB_BLOCK_CALL_FUNC_ARGLIST(elt, klass)) -{ - return rb_yield(rb_class_new_instance(1, &elt, klass)); -} - -/* - * Iterates over the entries (files and subdirectories) in the directory, - * yielding a Pathname object for each entry. - */ -static VALUE -path_each_entry(VALUE self) -{ - VALUE args[1]; - RETURN_ENUMERATOR(self, 0, 0); - - args[0] = get_strpath(self); - return rb_block_call(rb_cDir, id_foreach, 1, args, each_entry_i, rb_obj_class(self)); -} - -static VALUE -unlink_body(VALUE str) -{ - return rb_funcall(rb_cDir, id_unlink, 1, str); -} - -static VALUE -unlink_rescue(VALUE str, VALUE errinfo) -{ - return rb_funcall(rb_cFile, id_unlink, 1, str); -} - -/* - * Removes a file or directory, using File.unlink if +self+ is a file, or - * Dir.unlink as necessary. - */ -static VALUE -path_unlink(VALUE self) -{ - VALUE eENOTDIR = rb_const_get_at(rb_mErrno, id_ENOTDIR); - VALUE str = get_strpath(self); - return rb_rescue2(unlink_body, str, unlink_rescue, str, eENOTDIR, (VALUE)0); -} - -/* - * :call-seq: - * Pathname(path) -> pathname - * - * Creates a new Pathname object from the given string, +path+, and returns - * pathname object. - * - * In order to use this constructor, you must first require the Pathname - * standard library extension. - * - * require 'pathname' - * Pathname("/home/zzak") - * #=> #<Pathname:/home/zzak> - * - * See also Pathname::new for more information. - */ -static VALUE -path_f_pathname(VALUE self, VALUE str) -{ - if (CLASS_OF(str) == rb_cPathname) - return str; - return rb_class_new_instance(1, &str, rb_cPathname); -} - -/* - * - * Pathname represents the name of a file or directory on the filesystem, - * but not the file itself. - * - * The pathname depends on the Operating System: Unix, Windows, etc. - * This library works with pathnames of local OS, however non-Unix pathnames - * are supported experimentally. - * - * A Pathname can be relative or absolute. It's not until you try to - * reference the file that it even matters whether the file exists or not. - * - * Pathname is immutable. It has no method for destructive update. - * - * The goal of this class is to manipulate file path information in a neater - * way than standard Ruby provides. The examples below demonstrate the - * difference. - * - * *All* functionality from File, FileTest, and some from Dir and FileUtils is - * included, in an unsurprising way. It is essentially a facade for all of - * these, and more. - * - * == Examples - * - * === Example 1: Using Pathname - * - * require 'pathname' - * pn = Pathname.new("/usr/bin/ruby") - * size = pn.size # 27662 - * isdir = pn.directory? # false - * dir = pn.dirname # Pathname:/usr/bin - * base = pn.basename # Pathname:ruby - * dir, base = pn.split # [Pathname:/usr/bin, Pathname:ruby] - * data = pn.read - * pn.open { |f| _ } - * pn.each_line { |line| _ } - * - * === Example 2: Using standard Ruby - * - * pn = "/usr/bin/ruby" - * size = File.size(pn) # 27662 - * isdir = File.directory?(pn) # false - * dir = File.dirname(pn) # "/usr/bin" - * base = File.basename(pn) # "ruby" - * dir, base = File.split(pn) # ["/usr/bin", "ruby"] - * data = File.read(pn) - * File.open(pn) { |f| _ } - * File.foreach(pn) { |line| _ } - * - * === Example 3: Special features - * - * p1 = Pathname.new("/usr/lib") # Pathname:/usr/lib - * p2 = p1 + "ruby/1.8" # Pathname:/usr/lib/ruby/1.8 - * p3 = p1.parent # Pathname:/usr - * p4 = p2.relative_path_from(p3) # Pathname:lib/ruby/1.8 - * pwd = Pathname.pwd # Pathname:/home/gavin - * pwd.absolute? # true - * p5 = Pathname.new "." # Pathname:. - * p5 = p5 + "music/../articles" # Pathname:music/../articles - * p5.cleanpath # Pathname:articles - * p5.realpath # Pathname:/home/gavin/articles - * p5.children # [Pathname:/home/gavin/articles/linux, ...] - * - * == Breakdown of functionality - * - * === Core methods - * - * These methods are effectively manipulating a String, because that's - * all a path is. None of these access the file system except for - * #mountpoint?, #children, #each_child, #realdirpath and #realpath. - * - * - + - * - #join - * - #parent - * - #root? - * - #absolute? - * - #relative? - * - #relative_path_from - * - #each_filename - * - #cleanpath - * - #realpath - * - #realdirpath - * - #children - * - #each_child - * - #mountpoint? - * - * === File status predicate methods - * - * These methods are a facade for FileTest: - * - #blockdev? - * - #chardev? - * - #directory? - * - #executable? - * - #executable_real? - * - #exist? - * - #file? - * - #grpowned? - * - #owned? - * - #pipe? - * - #readable? - * - #world_readable? - * - #readable_real? - * - #setgid? - * - #setuid? - * - #size - * - #size? - * - #socket? - * - #sticky? - * - #symlink? - * - #writable? - * - #world_writable? - * - #writable_real? - * - #zero? - * - * === File property and manipulation methods - * - * These methods are a facade for File: - * - #atime - * - #birthtime - * - #ctime - * - #mtime - * - #chmod(mode) - * - #lchmod(mode) - * - #chown(owner, group) - * - #lchown(owner, group) - * - #fnmatch(pattern, *args) - * - #fnmatch?(pattern, *args) - * - #ftype - * - #make_link(old) - * - #open(*args, &block) - * - #readlink - * - #rename(to) - * - #stat - * - #lstat - * - #make_symlink(old) - * - #truncate(length) - * - #utime(atime, mtime) - * - #lutime(atime, mtime) - * - #basename(*args) - * - #dirname - * - #extname - * - #expand_path(*args) - * - #split - * - * === Directory methods - * - * These methods are a facade for Dir: - * - Pathname.glob(*args) - * - Pathname.getwd / Pathname.pwd - * - #rmdir - * - #entries - * - #each_entry(&block) - * - #mkdir(*args) - * - #opendir(*args) - * - * === IO - * - * These methods are a facade for IO: - * - #each_line(*args, &block) - * - #read(*args) - * - #binread(*args) - * - #readlines(*args) - * - #sysopen(*args) - * - #write(*args) - * - #binwrite(*args) - * - * === Utilities - * - * These methods are a mixture of Find, FileUtils, and others: - * - #find(&block) - * - #mkpath - * - #rmtree - * - #unlink / #delete - * - * - * == Method documentation - * - * As the above section shows, most of the methods in Pathname are facades. The - * documentation for these methods generally just says, for instance, "See - * FileTest.writable?", as you should be familiar with the original method - * anyway, and its documentation (e.g. through +ri+) will contain more - * information. In some cases, a brief description will follow. - */ -void -Init_pathname(void) -{ -#ifdef HAVE_RB_EXT_RACTOR_SAFE - rb_ext_ractor_safe(true); -#endif - - InitVM(pathname); - - rb_cPathname = rb_define_class("Pathname", rb_cObject); - rb_define_method(rb_cPathname, "initialize", path_initialize, 1); - rb_define_method(rb_cPathname, "freeze", path_freeze, 0); - rb_define_method(rb_cPathname, "==", path_eq, 1); - rb_define_method(rb_cPathname, "===", path_eq, 1); - rb_define_method(rb_cPathname, "eql?", path_eq, 1); - rb_define_method(rb_cPathname, "<=>", path_cmp, 1); - rb_define_method(rb_cPathname, "hash", path_hash, 0); - rb_define_method(rb_cPathname, "to_s", path_to_s, 0); - rb_define_method(rb_cPathname, "to_path", path_to_s, 0); - rb_define_method(rb_cPathname, "inspect", path_inspect, 0); - rb_define_method(rb_cPathname, "sub", path_sub, -1); - rb_define_method(rb_cPathname, "sub_ext", path_sub_ext, 1); - rb_define_method(rb_cPathname, "realpath", path_realpath, -1); - rb_define_method(rb_cPathname, "realdirpath", path_realdirpath, -1); - rb_define_method(rb_cPathname, "each_line", path_each_line, -1); - rb_define_method(rb_cPathname, "read", path_read, -1); - rb_define_method(rb_cPathname, "binread", path_binread, -1); - rb_define_method(rb_cPathname, "readlines", path_readlines, -1); - rb_define_method(rb_cPathname, "write", path_write, -1); - rb_define_method(rb_cPathname, "binwrite", path_binwrite, -1); - rb_define_method(rb_cPathname, "sysopen", path_sysopen, -1); - rb_define_method(rb_cPathname, "atime", path_atime, 0); - rb_define_method(rb_cPathname, "birthtime", path_birthtime, 0); - rb_define_method(rb_cPathname, "ctime", path_ctime, 0); - rb_define_method(rb_cPathname, "mtime", path_mtime, 0); - rb_define_method(rb_cPathname, "chmod", path_chmod, 1); - rb_define_method(rb_cPathname, "lchmod", path_lchmod, 1); - rb_define_method(rb_cPathname, "chown", path_chown, 2); - rb_define_method(rb_cPathname, "lchown", path_lchown, 2); - rb_define_method(rb_cPathname, "fnmatch", path_fnmatch, -1); - rb_define_method(rb_cPathname, "fnmatch?", path_fnmatch, -1); - rb_define_method(rb_cPathname, "ftype", path_ftype, 0); - rb_define_method(rb_cPathname, "make_link", path_make_link, 1); - rb_define_method(rb_cPathname, "open", path_open, -1); - rb_define_method(rb_cPathname, "readlink", path_readlink, 0); - rb_define_method(rb_cPathname, "rename", path_rename, 1); - rb_define_method(rb_cPathname, "stat", path_stat, 0); - rb_define_method(rb_cPathname, "lstat", path_lstat, 0); - rb_define_method(rb_cPathname, "make_symlink", path_make_symlink, 1); - rb_define_method(rb_cPathname, "truncate", path_truncate, 1); - rb_define_method(rb_cPathname, "utime", path_utime, 2); - rb_define_method(rb_cPathname, "lutime", path_lutime, 2); - rb_define_method(rb_cPathname, "basename", path_basename, -1); - rb_define_method(rb_cPathname, "dirname", path_dirname, 0); - rb_define_method(rb_cPathname, "extname", path_extname, 0); - rb_define_method(rb_cPathname, "expand_path", path_expand_path, -1); - rb_define_method(rb_cPathname, "split", path_split, 0); - rb_define_method(rb_cPathname, "blockdev?", path_blockdev_p, 0); - rb_define_method(rb_cPathname, "chardev?", path_chardev_p, 0); - rb_define_method(rb_cPathname, "executable?", path_executable_p, 0); - rb_define_method(rb_cPathname, "executable_real?", path_executable_real_p, 0); - rb_define_method(rb_cPathname, "exist?", path_exist_p, 0); - rb_define_method(rb_cPathname, "grpowned?", path_grpowned_p, 0); - rb_define_method(rb_cPathname, "directory?", path_directory_p, 0); - rb_define_method(rb_cPathname, "file?", path_file_p, 0); - rb_define_method(rb_cPathname, "pipe?", path_pipe_p, 0); - rb_define_method(rb_cPathname, "socket?", path_socket_p, 0); - rb_define_method(rb_cPathname, "owned?", path_owned_p, 0); - rb_define_method(rb_cPathname, "readable?", path_readable_p, 0); - rb_define_method(rb_cPathname, "world_readable?", path_world_readable_p, 0); - rb_define_method(rb_cPathname, "readable_real?", path_readable_real_p, 0); - rb_define_method(rb_cPathname, "setuid?", path_setuid_p, 0); - rb_define_method(rb_cPathname, "setgid?", path_setgid_p, 0); - rb_define_method(rb_cPathname, "size", path_size, 0); - rb_define_method(rb_cPathname, "size?", path_size_p, 0); - rb_define_method(rb_cPathname, "sticky?", path_sticky_p, 0); - rb_define_method(rb_cPathname, "symlink?", path_symlink_p, 0); - rb_define_method(rb_cPathname, "writable?", path_writable_p, 0); - rb_define_method(rb_cPathname, "world_writable?", path_world_writable_p, 0); - rb_define_method(rb_cPathname, "writable_real?", path_writable_real_p, 0); - rb_define_method(rb_cPathname, "zero?", path_zero_p, 0); - rb_define_method(rb_cPathname, "empty?", path_empty_p, 0); - rb_define_singleton_method(rb_cPathname, "glob", path_s_glob, -1); - rb_define_singleton_method(rb_cPathname, "getwd", path_s_getwd, 0); - rb_define_singleton_method(rb_cPathname, "pwd", path_s_getwd, 0); - rb_define_method(rb_cPathname, "glob", path_glob, -1); - rb_define_method(rb_cPathname, "entries", path_entries, 0); - rb_define_method(rb_cPathname, "mkdir", path_mkdir, -1); - rb_define_method(rb_cPathname, "rmdir", path_rmdir, 0); - rb_define_method(rb_cPathname, "opendir", path_opendir, 0); - rb_define_method(rb_cPathname, "each_entry", path_each_entry, 0); - rb_define_method(rb_cPathname, "unlink", path_unlink, 0); - rb_define_method(rb_cPathname, "delete", path_unlink, 0); - rb_undef_method(rb_cPathname, "=~"); - rb_define_global_function("Pathname", path_f_pathname, 1); -} - -void -InitVM_pathname(void) -{ -#undef rb_intern - id_at_path = rb_intern("@path"); - id_to_path = rb_intern("to_path"); - id_ENOTDIR = rb_intern("ENOTDIR"); - id_atime = rb_intern("atime"); - id_basename = rb_intern("basename"); - id_base = rb_intern("base"); - id_binread = rb_intern("binread"); - id_binwrite = rb_intern("binwrite"); - id_birthtime = rb_intern("birthtime"); - id_blockdev_p = rb_intern("blockdev?"); - id_chardev_p = rb_intern("chardev?"); - id_chmod = rb_intern("chmod"); - id_chown = rb_intern("chown"); - id_ctime = rb_intern("ctime"); - id_directory_p = rb_intern("directory?"); - id_dirname = rb_intern("dirname"); - id_empty_p = rb_intern("empty?"); - id_entries = rb_intern("entries"); - id_executable_p = rb_intern("executable?"); - id_executable_real_p = rb_intern("executable_real?"); - id_exist_p = rb_intern("exist?"); - id_expand_path = rb_intern("expand_path"); - id_extname = rb_intern("extname"); - id_file_p = rb_intern("file?"); - id_fnmatch = rb_intern("fnmatch"); - id_foreach = rb_intern("foreach"); - id_ftype = rb_intern("ftype"); - id_getwd = rb_intern("getwd"); - id_glob = rb_intern("glob"); - id_grpowned_p = rb_intern("grpowned?"); - id_lchmod = rb_intern("lchmod"); - id_lchown = rb_intern("lchown"); - id_link = rb_intern("link"); - id_lstat = rb_intern("lstat"); - id_lutime = rb_intern("lutime"); - id_mkdir = rb_intern("mkdir"); - id_mtime = rb_intern("mtime"); - id_open = rb_intern("open"); - id_owned_p = rb_intern("owned?"); - id_pipe_p = rb_intern("pipe?"); - id_read = rb_intern("read"); - id_readable_p = rb_intern("readable?"); - id_readable_real_p = rb_intern("readable_real?"); - id_readlines = rb_intern("readlines"); - id_readlink = rb_intern("readlink"); - id_realdirpath = rb_intern("realdirpath"); - id_realpath = rb_intern("realpath"); - id_rename = rb_intern("rename"); - id_rmdir = rb_intern("rmdir"); - id_setgid_p = rb_intern("setgid?"); - id_setuid_p = rb_intern("setuid?"); - id_size = rb_intern("size"); - id_size_p = rb_intern("size?"); - id_socket_p = rb_intern("socket?"); - id_split = rb_intern("split"); - id_stat = rb_intern("stat"); - id_sticky_p = rb_intern("sticky?"); - id_sub = rb_intern("sub"); - id_symlink = rb_intern("symlink"); - id_symlink_p = rb_intern("symlink?"); - id_sysopen = rb_intern("sysopen"); - id_truncate = rb_intern("truncate"); - id_unlink = rb_intern("unlink"); - id_utime = rb_intern("utime"); - id_world_readable_p = rb_intern("world_readable?"); - id_world_writable_p = rb_intern("world_writable?"); - id_writable_p = rb_intern("writable?"); - id_writable_real_p = rb_intern("writable_real?"); - id_write = rb_intern("write"); - id_zero_p = rb_intern("zero?"); -} diff --git a/ext/pathname/pathname.gemspec b/ext/pathname/pathname.gemspec deleted file mode 100644 index 890bc2fde9..0000000000 --- a/ext/pathname/pathname.gemspec +++ /dev/null @@ -1,32 +0,0 @@ -name = File.basename(__FILE__, ".gemspec") -version = ["lib", "ext/lib"].find do |dir| - break File.foreach(File.join(__dir__, dir, "#{name}.rb")) do |line| - /^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1 - end rescue nil -end - -Gem::Specification.new do |spec| - spec.name = name - spec.version = version - spec.authors = ["Tanaka Akira"] - spec.email = ["akr@fsij.org"] - - spec.summary = %q{Representation of the name of a file or directory on the filesystem} - spec.description = %q{Representation of the name of a file or directory on the filesystem} - spec.homepage = "https://github.com/ruby/pathname" - spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0") - spec.licenses = ["Ruby", "BSD-2-Clause"] - - spec.metadata["homepage_uri"] = spec.homepage - spec.metadata["source_code_uri"] = spec.homepage - - # Specify which files should be added to the gem when it is released. - # The `git ls-files -z` loads the files in the RubyGem that have been added into git. - spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } - end - spec.bindir = "exe" - spec.executables = [] - spec.require_paths = ["lib"] - spec.extensions = %w[ext/pathname/extconf.rb] -end diff --git a/ext/psych/depend b/ext/psych/depend index 13fbe3fb19..95175841a2 100644 --- a/ext/psych/depend +++ b/ext/psych/depend @@ -152,6 +152,7 @@ psych.o: $(hdrdir)/ruby/internal/intern/re.h psych.o: $(hdrdir)/ruby/internal/intern/ruby.h psych.o: $(hdrdir)/ruby/internal/intern/select.h psych.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +psych.o: $(hdrdir)/ruby/internal/intern/set.h psych.o: $(hdrdir)/ruby/internal/intern/signal.h psych.o: $(hdrdir)/ruby/internal/intern/sprintf.h psych.o: $(hdrdir)/ruby/internal/intern/string.h @@ -171,6 +172,7 @@ psych.o: $(hdrdir)/ruby/internal/special_consts.h psych.o: $(hdrdir)/ruby/internal/static_assert.h psych.o: $(hdrdir)/ruby/internal/stdalign.h psych.o: $(hdrdir)/ruby/internal/stdbool.h +psych.o: $(hdrdir)/ruby/internal/stdckdint.h psych.o: $(hdrdir)/ruby/internal/symbol.h psych.o: $(hdrdir)/ruby/internal/value.h psych.o: $(hdrdir)/ruby/internal/value_type.h @@ -328,6 +330,7 @@ psych_emitter.o: $(hdrdir)/ruby/internal/intern/re.h psych_emitter.o: $(hdrdir)/ruby/internal/intern/ruby.h psych_emitter.o: $(hdrdir)/ruby/internal/intern/select.h psych_emitter.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +psych_emitter.o: $(hdrdir)/ruby/internal/intern/set.h psych_emitter.o: $(hdrdir)/ruby/internal/intern/signal.h psych_emitter.o: $(hdrdir)/ruby/internal/intern/sprintf.h psych_emitter.o: $(hdrdir)/ruby/internal/intern/string.h @@ -347,6 +350,7 @@ psych_emitter.o: $(hdrdir)/ruby/internal/special_consts.h psych_emitter.o: $(hdrdir)/ruby/internal/static_assert.h psych_emitter.o: $(hdrdir)/ruby/internal/stdalign.h psych_emitter.o: $(hdrdir)/ruby/internal/stdbool.h +psych_emitter.o: $(hdrdir)/ruby/internal/stdckdint.h psych_emitter.o: $(hdrdir)/ruby/internal/symbol.h psych_emitter.o: $(hdrdir)/ruby/internal/value.h psych_emitter.o: $(hdrdir)/ruby/internal/value_type.h @@ -504,6 +508,7 @@ psych_parser.o: $(hdrdir)/ruby/internal/intern/re.h psych_parser.o: $(hdrdir)/ruby/internal/intern/ruby.h psych_parser.o: $(hdrdir)/ruby/internal/intern/select.h psych_parser.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +psych_parser.o: $(hdrdir)/ruby/internal/intern/set.h psych_parser.o: $(hdrdir)/ruby/internal/intern/signal.h psych_parser.o: $(hdrdir)/ruby/internal/intern/sprintf.h psych_parser.o: $(hdrdir)/ruby/internal/intern/string.h @@ -523,6 +528,7 @@ psych_parser.o: $(hdrdir)/ruby/internal/special_consts.h psych_parser.o: $(hdrdir)/ruby/internal/static_assert.h psych_parser.o: $(hdrdir)/ruby/internal/stdalign.h psych_parser.o: $(hdrdir)/ruby/internal/stdbool.h +psych_parser.o: $(hdrdir)/ruby/internal/stdckdint.h psych_parser.o: $(hdrdir)/ruby/internal/symbol.h psych_parser.o: $(hdrdir)/ruby/internal/value.h psych_parser.o: $(hdrdir)/ruby/internal/value_type.h @@ -680,6 +686,7 @@ psych_to_ruby.o: $(hdrdir)/ruby/internal/intern/re.h psych_to_ruby.o: $(hdrdir)/ruby/internal/intern/ruby.h psych_to_ruby.o: $(hdrdir)/ruby/internal/intern/select.h psych_to_ruby.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +psych_to_ruby.o: $(hdrdir)/ruby/internal/intern/set.h psych_to_ruby.o: $(hdrdir)/ruby/internal/intern/signal.h psych_to_ruby.o: $(hdrdir)/ruby/internal/intern/sprintf.h psych_to_ruby.o: $(hdrdir)/ruby/internal/intern/string.h @@ -699,6 +706,7 @@ psych_to_ruby.o: $(hdrdir)/ruby/internal/special_consts.h psych_to_ruby.o: $(hdrdir)/ruby/internal/static_assert.h psych_to_ruby.o: $(hdrdir)/ruby/internal/stdalign.h psych_to_ruby.o: $(hdrdir)/ruby/internal/stdbool.h +psych_to_ruby.o: $(hdrdir)/ruby/internal/stdckdint.h psych_to_ruby.o: $(hdrdir)/ruby/internal/symbol.h psych_to_ruby.o: $(hdrdir)/ruby/internal/value.h psych_to_ruby.o: $(hdrdir)/ruby/internal/value_type.h @@ -856,6 +864,7 @@ psych_yaml_tree.o: $(hdrdir)/ruby/internal/intern/re.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/intern/ruby.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/intern/select.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +psych_yaml_tree.o: $(hdrdir)/ruby/internal/intern/set.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/intern/signal.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/intern/sprintf.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/intern/string.h @@ -875,6 +884,7 @@ psych_yaml_tree.o: $(hdrdir)/ruby/internal/special_consts.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/static_assert.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/stdalign.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/stdbool.h +psych_yaml_tree.o: $(hdrdir)/ruby/internal/stdckdint.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/symbol.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/value.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/psych/extconf.rb b/ext/psych/extconf.rb index e7dd0bb60a..589e201c1c 100644 --- a/ext/psych/extconf.rb +++ b/ext/psych/extconf.rb @@ -36,8 +36,11 @@ if yaml_source libyaml = "libyaml.#$LIBEXT" $cleanfiles << libyaml $LOCAL_LIBS.prepend("$(LIBYAML) ") -else # default to pre-installed libyaml - pkg_config('yaml-0.1') + + # default to pre-installed libyaml +elsif pkg_config('yaml-0.1') + # found with pkg-config +else dir_config('libyaml') find_header('yaml.h') or abort "yaml.h not found" find_library('yaml', 'yaml_get_version') or abort "libyaml not found" diff --git a/ext/psych/lib/psych.rb b/ext/psych/lib/psych.rb index d87bd9040a..850a6d1937 100644 --- a/ext/psych/lib/psych.rb +++ b/ext/psych/lib/psych.rb @@ -1,4 +1,6 @@ # frozen_string_literal: true +require 'date' + require_relative 'psych/versions' case RUBY_ENGINE when 'jruby' @@ -21,7 +23,6 @@ require_relative 'psych/parser' require_relative 'psych/omap' require_relative 'psych/set' require_relative 'psych/coder' -require_relative 'psych/core_ext' require_relative 'psych/stream' require_relative 'psych/json/tree_builder' require_relative 'psych/json/stream' @@ -84,7 +85,7 @@ require_relative 'psych/class_loader' # Psych.safe_load_file("data.yml", permitted_classes: [Date]) # Psych.load_file("trusted_database.yml") # -# ==== Exception handling +# ==== \Exception handling # # begin # # The second argument changes only the exception contents @@ -148,7 +149,7 @@ require_relative 'psych/class_loader' # # Returns Psych::Nodes::Document # Psych.parse_file('database.yml') # -# ==== Exception handling +# ==== \Exception handling # # begin # # The second argument changes only the exception contents @@ -268,10 +269,10 @@ module Psych # YAML documents that are supplied via user input. Instead, please use the # load method or the safe_load method. # - def self.unsafe_load yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false, strict_integer: false + def self.unsafe_load yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true result = parse(yaml, filename: filename) return fallback unless result - result.to_ruby(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer) + result.to_ruby(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer, parse_symbols: parse_symbols) end ### @@ -319,13 +320,13 @@ module Psych # Psych.safe_load("---\n foo: bar") # => {"foo"=>"bar"} # Psych.safe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"} # - def self.safe_load yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false + def self.safe_load yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true result = parse(yaml, filename: filename) return fallback unless result class_loader = ClassLoader::Restricted.new(permitted_classes.map(&:to_s), permitted_symbols.map(&:to_s)) - scanner = ScalarScanner.new class_loader, strict_integer: strict_integer + scanner = ScalarScanner.new class_loader, strict_integer: strict_integer, parse_symbols: parse_symbols visitor = if aliases Visitors::ToRuby.new scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze else @@ -340,7 +341,7 @@ module Psych # provided, the object contained in the first document will be returned. # +filename+ will be used in the exception message if any exception # is raised while parsing. If +yaml+ is empty, it returns - # the specified +fallback+ return value, which defaults to +false+. + # the specified +fallback+ return value, which defaults to +nil+. # # Raises a Psych::SyntaxError when a YAML syntax error is detected. # @@ -365,7 +366,7 @@ module Psych # Raises a TypeError when `yaml` parameter is NilClass. This method is # similar to `safe_load` except that `Symbol` objects are allowed by default. # - def self.load yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false + def self.load yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true safe_load yaml, permitted_classes: permitted_classes, permitted_symbols: permitted_symbols, aliases: aliases, @@ -373,7 +374,8 @@ module Psych fallback: fallback, symbolize_names: symbolize_names, freeze: freeze, - strict_integer: strict_integer + strict_integer: strict_integer, + parse_symbols: parse_symbols end ### @@ -479,6 +481,7 @@ module Psych # # Default: <tt>2</tt>. # [<tt>:line_width</tt>] Max character to wrap line at. + # For unlimited line width use <tt>-1</tt>. # # Default: <tt>0</tt> (meaning "wrap at 81"). # [<tt>:canonical</tt>] Write "canonical" YAML form (very verbose, yet @@ -559,6 +562,7 @@ module Psych # # Default: <tt>2</tt>. # [<tt>:line_width</tt>] Max character to wrap line at. + # For unlimited line width use <tt>-1</tt>. # # Default: <tt>0</tt> (meaning "wrap at 81"). # [<tt>:canonical</tt>] Write "canonical" YAML form (very verbose, yet @@ -651,6 +655,35 @@ module Psych end ### + # Load multiple documents given in +yaml+. Returns the parsed documents + # as a list. + # + # Example: + # + # Psych.safe_load_stream("--- foo\n...\n--- bar\n...") # => ['foo', 'bar'] + # + # list = [] + # Psych.safe_load_stream("--- foo\n...\n--- bar\n...") do |ruby| + # list << ruby + # end + # list # => ['foo', 'bar'] + # + def self.safe_load_stream yaml, filename: nil, permitted_classes: [], aliases: false + documents = parse_stream(yaml, filename: filename).children.map do |child| + stream = Psych::Nodes::Stream.new + stream.children << child + safe_load(stream.to_yaml, permitted_classes: permitted_classes, aliases: aliases) + end + + if block_given? + documents.each { |doc| yield doc } + nil + else + documents + end + end + + ### # Load the document contained in +filename+. Returns the yaml contained in # +filename+ as a Ruby object, or if the file is empty, it returns # the specified +fallback+ return value, which defaults to +false+. @@ -667,7 +700,7 @@ module Psych ### # Safely loads the document contained in +filename+. Returns the yaml contained in # +filename+ as a Ruby object, or if the file is empty, it returns - # the specified +fallback+ return value, which defaults to +false+. + # the specified +fallback+ return value, which defaults to +nil+. # See safe_load for options. def self.safe_load_file filename, **kwargs File.open(filename, 'r:bom|utf-8') { |f| @@ -678,7 +711,7 @@ module Psych ### # Loads the document contained in +filename+. Returns the yaml contained in # +filename+ as a Ruby object, or if the file is empty, it returns - # the specified +fallback+ return value, which defaults to +false+. + # the specified +fallback+ return value, which defaults to +nil+. # See load for options. def self.load_file filename, **kwargs File.open(filename, 'r:bom|utf-8') { |f| @@ -757,3 +790,5 @@ module Psych self.domain_types = {} # :startdoc: end + +require_relative 'psych/core_ext' diff --git a/ext/psych/lib/psych/class_loader.rb b/ext/psych/lib/psych/class_loader.rb index 50efc35ee2..c8f509720a 100644 --- a/ext/psych/lib/psych/class_loader.rb +++ b/ext/psych/lib/psych/class_loader.rb @@ -6,6 +6,7 @@ module Psych class ClassLoader # :nodoc: BIG_DECIMAL = 'BigDecimal' COMPLEX = 'Complex' + DATA = 'Data' unless RUBY_VERSION < "3.2" DATE = 'Date' DATE_TIME = 'DateTime' EXCEPTION = 'Exception' diff --git a/ext/psych/lib/psych/core_ext.rb b/ext/psych/lib/psych/core_ext.rb index 0721a133c3..6dfd0f1696 100644 --- a/ext/psych/lib/psych/core_ext.rb +++ b/ext/psych/lib/psych/core_ext.rb @@ -14,6 +14,23 @@ class Object end end -if defined?(::IRB) - require_relative 'y' +# Up to Ruby 3.4, Set was a regular object and was dumped as such +# by Pysch. +# Starting from Ruby 4.0 it's a core class written in C, so we have to implement +# #encode_with / #init_with to preserve backward compatibility. +if defined?(::Set) && Set.new.instance_variables.empty? + class Set + def encode_with(coder) + hash = {} + each do |m| + hash[m] = true + end + coder["hash"] = hash + coder + end + + def init_with(coder) + replace(coder["hash"].keys) + end + end end diff --git a/ext/psych/lib/psych/nodes/node.rb b/ext/psych/lib/psych/nodes/node.rb index f44fce5f05..fc27448f2e 100644 --- a/ext/psych/lib/psych/nodes/node.rb +++ b/ext/psych/lib/psych/nodes/node.rb @@ -1,5 +1,4 @@ # frozen_string_literal: true -require 'stringio' require_relative '../class_loader' require_relative '../scalar_scanner' @@ -46,8 +45,8 @@ module Psych # Convert this node to Ruby. # # See also Psych::Visitors::ToRuby - def to_ruby(symbolize_names: false, freeze: false, strict_integer: false) - Visitors::ToRuby.create(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer).accept(self) + def to_ruby(symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true) + Visitors::ToRuby.create(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer, parse_symbols: parse_symbols).accept(self) end alias :transform :to_ruby @@ -56,6 +55,8 @@ module Psych # # See also Psych::Visitors::Emitter def yaml io = nil, options = {} + require "stringio" unless defined?(StringIO) + real_io = io || StringIO.new(''.encode('utf-8')) Visitors::Emitter.new(real_io, options).accept self diff --git a/ext/psych/lib/psych/scalar_scanner.rb b/ext/psych/lib/psych/scalar_scanner.rb index 3cb4bf3c7e..6a556fb3b8 100644 --- a/ext/psych/lib/psych/scalar_scanner.rb +++ b/ext/psych/lib/psych/scalar_scanner.rb @@ -11,26 +11,27 @@ module Psych # Base 60, [-+]inf and NaN are handled separately FLOAT = /^(?:[-+]?([0-9][0-9_,]*)?\.[0-9]*([eE][-+][0-9]+)?(?# base 10))$/x - # Taken from http://yaml.org/type/int.html - INTEGER_STRICT = /^(?:[-+]?0b[0-1_]+ (?# base 2) - |[-+]?0[0-7_]+ (?# base 8) - |[-+]?(0|[1-9][0-9_]*) (?# base 10) - |[-+]?0x[0-9a-fA-F_]+ (?# base 16))$/x + # Taken from http://yaml.org/type/int.html and modified to ensure at least one numerical symbol exists + INTEGER_STRICT = /^(?:[-+]?0b[_]*[0-1][0-1_]* (?# base 2) + |[-+]?0[_]*[0-7][0-7_]* (?# base 8) + |[-+]?(0|[1-9][0-9_]*) (?# base 10) + |[-+]?0x[_]*[0-9a-fA-F][0-9a-fA-F_]* (?# base 16))$/x # Same as above, but allows commas. # Not to YML spec, but kept for backwards compatibility - INTEGER_LEGACY = /^(?:[-+]?0b[0-1_,]+ (?# base 2) - |[-+]?0[0-7_,]+ (?# base 8) + INTEGER_LEGACY = /^(?:[-+]?0b[_,]*[0-1][0-1_,]* (?# base 2) + |[-+]?0[_,]*[0-7][0-7_,]* (?# base 8) |[-+]?(?:0|[1-9](?:[0-9]|,[0-9]|_[0-9])*) (?# base 10) - |[-+]?0x[0-9a-fA-F_,]+ (?# base 16))$/x + |[-+]?0x[_,]*[0-9a-fA-F][0-9a-fA-F_,]* (?# base 16))$/x attr_reader :class_loader # Create a new scanner - def initialize class_loader, strict_integer: false + def initialize class_loader, strict_integer: false, parse_symbols: true @symbol_cache = {} @class_loader = class_loader @strict_integer = strict_integer + @parse_symbols = parse_symbols end # Tokenize +string+ returning the Ruby object @@ -61,7 +62,6 @@ module Psych string end elsif string.match?(/^\d{4}-(?:1[012]|0\d|\d)-(?:[12]\d|3[01]|0\d|\d)$/) - require 'date' begin class_loader.date.strptime(string, '%F', Date::GREGORIAN) rescue ArgumentError @@ -73,7 +73,7 @@ module Psych -Float::INFINITY elsif string.match?(/^\.nan$/i) Float::NAN - elsif string.match?(/^:./) + elsif @parse_symbols && string.match?(/^:./) if string =~ /^:(["'])(.*)\1/ @symbol_cache[string] = class_loader.symbolize($2.sub(/^:/, '')) else diff --git a/ext/psych/lib/psych/versions.rb b/ext/psych/lib/psych/versions.rb index b9e8d9ef11..4c7a80d5c8 100644 --- a/ext/psych/lib/psych/versions.rb +++ b/ext/psych/lib/psych/versions.rb @@ -2,9 +2,9 @@ module Psych # The version of Psych you are using - VERSION = '5.1.2' + VERSION = '5.3.1' if RUBY_ENGINE == 'jruby' - DEFAULT_SNAKEYAML_VERSION = '2.7'.freeze + DEFAULT_SNAKEYAML_VERSION = '2.10'.freeze end end diff --git a/ext/psych/lib/psych/visitors/to_ruby.rb b/ext/psych/lib/psych/visitors/to_ruby.rb index f0fda9bdbc..475444e589 100644 --- a/ext/psych/lib/psych/visitors/to_ruby.rb +++ b/ext/psych/lib/psych/visitors/to_ruby.rb @@ -12,9 +12,13 @@ module Psych ### # This class walks a YAML AST, converting each node to Ruby class ToRuby < Psych::Visitors::Visitor - def self.create(symbolize_names: false, freeze: false, strict_integer: false) + unless RUBY_VERSION < "3.2" + DATA_INITIALIZE = Data.instance_method(:initialize) + end + + def self.create(symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true) class_loader = ClassLoader.new - scanner = ScalarScanner.new class_loader, strict_integer: strict_integer + scanner = ScalarScanner.new class_loader, strict_integer: strict_integer, parse_symbols: parse_symbols new(scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze) end @@ -36,7 +40,7 @@ module Psych unless @domain_types.empty? || !target.tag key = target.tag.sub(/^[!\/]*/, '').sub(/(,\d+)\//, '\1:') - key = "tag:#{key}" unless key =~ /^(?:tag:|x-private)/ + key = "tag:#{key}" unless key.match?(/^(?:tag:|x-private)/) if @domain_types.key? key value, block = @domain_types[key] @@ -79,7 +83,6 @@ module Psych class_loader.big_decimal._load o.value when "!ruby/object:DateTime" class_loader.date_time - require 'date' unless defined? DateTime t = @ss.parse_time(o.value) DateTime.civil(*t.to_a[0, 6].reverse, Rational(t.utc_offset, 86400)) + (t.subsec/86400) @@ -97,11 +100,11 @@ module Psych Float(@ss.tokenize(o.value)) when "!ruby/regexp" klass = class_loader.regexp - o.value =~ /^\/(.*)\/([mixn]*)$/m - source = $1 + matches = /^\/(?<string>.*)\/(?<options>[mixn]*)$/m.match(o.value) + source = matches[:string].gsub('\/', '/') options = 0 lang = nil - $2&.each_char do |option| + matches[:options].each_char do |option| case option when 'x' then options |= Regexp::EXTENDED when 'i' then options |= Regexp::IGNORECASE @@ -198,6 +201,32 @@ module Psych s end + when /^!ruby\/data(-with-ivars)?(?::(.*))?$/ + data = register(o, resolve_class($2).allocate) if $2 + members = {} + + if $1 # data-with-ivars + ivars = {} + o.children.each_slice(2) do |type, vars| + case accept(type) + when 'members' + revive_data_members(members, vars) + data ||= allocate_anon_data(o, members) + when 'ivars' + revive_hash(ivars, vars) + end + end + ivars.each do |ivar, v| + data.instance_variable_set ivar, v + end + else + revive_data_members(members, o) + end + data ||= allocate_anon_data(o, members) + DATA_INITIALIZE.bind_call(data, **members) + data.freeze + data + when /^!ruby\/object:?(.*)?$/ name = $1 || 'Object' @@ -341,6 +370,20 @@ module Psych list end + def allocate_anon_data node, members + klass = class_loader.data.define(*members.keys) + register(node, klass.allocate) + end + + def revive_data_members hash, o + o.children.each_slice(2) do |k,v| + name = accept(k) + value = accept(v) + hash[class_loader.symbolize(name)] = value + end + hash + end + def revive_hash hash, o, tagged= false o.children.each_slice(2) { |k,v| key = accept(k) diff --git a/ext/psych/lib/psych/visitors/yaml_tree.rb b/ext/psych/lib/psych/visitors/yaml_tree.rb index a2ebc4d781..b6c86f4c94 100644 --- a/ext/psych/lib/psych/visitors/yaml_tree.rb +++ b/ext/psych/lib/psych/visitors/yaml_tree.rb @@ -73,7 +73,7 @@ module Psych method = respond_to?(method) ? method : h[klass.superclass] - raise(TypeError, "Can't dump #{target.class}") unless method + raise(TypeError, "can't dump #{klass.name}") unless method h[klass] = method end.compare_by_identity @@ -162,6 +162,44 @@ module Psych alias :visit_Delegator :visit_Object + def visit_Data o + ivars = o.instance_variables + if ivars.empty? + tag = ['!ruby/data', o.class.name].compact.join(':') + register o, @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK) + o.members.each do |member| + @emitter.scalar member.to_s, nil, nil, true, false, Nodes::Scalar::ANY + accept o.send member + end + @emitter.end_mapping + + else + tag = ['!ruby/data-with-ivars', o.class.name].compact.join(':') + node = @emitter.start_mapping(nil, tag, false, Psych::Nodes::Mapping::BLOCK) + register(o, node) + + # Dump the members + accept 'members' + @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK + o.members.each do |member| + @emitter.scalar member.to_s, nil, nil, true, false, Nodes::Scalar::ANY + accept o.send member + end + @emitter.end_mapping + + # Dump the ivars + accept 'ivars' + @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK + ivars.each do |ivar| + accept ivar.to_s + accept o.instance_variable_get ivar + end + @emitter.end_mapping + + @emitter.end_mapping + end + end unless RUBY_VERSION < "3.2" + def visit_Struct o tag = ['!ruby/struct', o.class.name].compact.join(':') @@ -189,7 +227,8 @@ module Psych end def visit_Date o - register o, visit_Integer(o.gregorian) + formatted = format_date o + register o, @emitter.scalar(formatted, nil, nil, true, false, Nodes::Scalar::ANY) end def visit_DateTime o @@ -261,7 +300,7 @@ module Psych style = Nodes::Scalar::LITERAL plain = false quote = false - elsif o =~ /\n(?!\Z)/ # match \n except blank line at the end of string + elsif o.match?(/\n(?!\Z)/) # match \n except blank line at the end of string style = Nodes::Scalar::LITERAL elsif o == '<<' style = Nodes::Scalar::SINGLE_QUOTED @@ -272,9 +311,9 @@ module Psych style = Nodes::Scalar::DOUBLE_QUOTED elsif @line_width && o.length > @line_width style = Nodes::Scalar::FOLDED - elsif o =~ /^[^[:word:]][^"]*$/ + elsif o.match?(/^[^[:word:]][^"]*$/) style = Nodes::Scalar::DOUBLE_QUOTED - elsif not String === @ss.tokenize(o) or /\A0[0-7]*[89]/ =~ o + elsif not String === @ss.tokenize(o) or /\A0[0-7]*[89]/.match?(o) style = Nodes::Scalar::SINGLE_QUOTED end @@ -486,6 +525,10 @@ module Psych end end + def format_date date + date.strftime("%Y-%m-%d") + end + def register target, yaml_obj @st.register target, yaml_obj yaml_obj diff --git a/ext/psych/psych.c b/ext/psych/psych.c index 8af0bb6a5a..afbd7a3571 100644 --- a/ext/psych/psych.c +++ b/ext/psych/psych.c @@ -23,7 +23,7 @@ VALUE mPsych; void Init_psych(void) { #ifdef HAVE_RB_EXT_RACTOR_SAFE - RB_EXT_RACTOR_SAFE(true); + RB_EXT_RACTOR_SAFE(true); #endif mPsych = rb_define_module("Psych"); @@ -34,4 +34,3 @@ void Init_psych(void) Init_psych_to_ruby(); Init_psych_yaml_tree(); } -/* vim: set noet sws=4 sw=4: */ diff --git a/ext/psych/psych.gemspec b/ext/psych/psych.gemspec index a3fc53a8b9..a32f79bc16 100644 --- a/ext/psych/psych.gemspec +++ b/ext/psych/psych.gemspec @@ -75,6 +75,8 @@ DESCRIPTION s.add_dependency 'stringio' end + s.add_dependency 'date' + s.metadata['msys2_mingw_dependencies'] = 'libyaml' s.metadata['changelog_uri'] = s.homepage + '/releases' end diff --git a/ext/psych/psych_emitter.c b/ext/psych/psych_emitter.c index 022ffa0946..624ab7c528 100644 --- a/ext/psych/psych_emitter.c +++ b/ext/psych/psych_emitter.c @@ -17,7 +17,7 @@ static ID id_canonical; static void emit(yaml_emitter_t * emitter, yaml_event_t * event) { if(!yaml_emitter_emit(emitter, event)) - rb_raise(rb_eRuntimeError, "%s", emitter->problem); + rb_raise(rb_eRuntimeError, "%s", emitter->problem); } static int writer(void *ctx, unsigned char *buffer, size_t size) @@ -82,13 +82,13 @@ static VALUE initialize(int argc, VALUE *argv, VALUE self) TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); if (rb_scan_args(argc, argv, "11", &io, &options) == 2) { - line_width = rb_funcall(options, id_line_width, 0); - indent = rb_funcall(options, id_indentation, 0); - canonical = rb_funcall(options, id_canonical, 0); + line_width = rb_funcall(options, id_line_width, 0); + indent = rb_funcall(options, id_indentation, 0); + canonical = rb_funcall(options, id_canonical, 0); - yaml_emitter_set_width(emitter, NUM2INT(line_width)); - yaml_emitter_set_indent(emitter, NUM2INT(indent)); - yaml_emitter_set_canonical(emitter, Qtrue == canonical ? 1 : 0); + yaml_emitter_set_width(emitter, NUM2INT(line_width)); + yaml_emitter_set_indent(emitter, NUM2INT(indent)); + yaml_emitter_set_canonical(emitter, Qtrue == canonical ? 1 : 0); } rb_ivar_set(self, id_io, io); @@ -136,84 +136,118 @@ static VALUE end_stream(VALUE self) return self; } -/* call-seq: emitter.start_document(version, tags, implicit) - * - * Start a document emission with YAML +version+, +tags+, and an +implicit+ - * start. - * - * See Psych::Handler#start_document - */ -static VALUE start_document(VALUE self, VALUE version, VALUE tags, VALUE imp) +struct start_document_data { + VALUE self; + VALUE version; + VALUE tags; + VALUE imp; + + yaml_tag_directive_t * head; +}; + +static VALUE start_document_try(VALUE d) { + struct start_document_data * data = (struct start_document_data *)d; + VALUE self = data->self; + VALUE version = data->version; + VALUE tags = data->tags; + VALUE imp = data->imp; + yaml_emitter_t * emitter; - yaml_tag_directive_t * head = NULL; yaml_tag_directive_t * tail = NULL; yaml_event_t event; yaml_version_directive_t version_directive; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); - Check_Type(version, T_ARRAY); if(RARRAY_LEN(version) > 0) { - VALUE major = rb_ary_entry(version, (long)0); - VALUE minor = rb_ary_entry(version, (long)1); + VALUE major = rb_ary_entry(version, (long)0); + VALUE minor = rb_ary_entry(version, (long)1); - version_directive.major = NUM2INT(major); - version_directive.minor = NUM2INT(minor); + version_directive.major = NUM2INT(major); + version_directive.minor = NUM2INT(minor); } if(RTEST(tags)) { - long i = 0; - long len; - rb_encoding * encoding = rb_utf8_encoding(); - - Check_Type(tags, T_ARRAY); - - len = RARRAY_LEN(tags); - head = xcalloc((size_t)len, sizeof(yaml_tag_directive_t)); - tail = head; - - for(i = 0; i < len && i < RARRAY_LEN(tags); i++) { - VALUE tuple = RARRAY_AREF(tags, i); - VALUE name; - VALUE value; - - Check_Type(tuple, T_ARRAY); - - if(RARRAY_LEN(tuple) < 2) { - xfree(head); - rb_raise(rb_eRuntimeError, "tag tuple must be of length 2"); - } - name = RARRAY_AREF(tuple, 0); - value = RARRAY_AREF(tuple, 1); - StringValue(name); - StringValue(value); - name = rb_str_export_to_enc(name, encoding); - value = rb_str_export_to_enc(value, encoding); - - tail->handle = (yaml_char_t *)StringValueCStr(name); - tail->prefix = (yaml_char_t *)StringValueCStr(value); - - tail++; - } + long i = 0; + long len; + rb_encoding * encoding = rb_utf8_encoding(); + + Check_Type(tags, T_ARRAY); + + len = RARRAY_LEN(tags); + data->head = xcalloc((size_t)len, sizeof(yaml_tag_directive_t)); + tail = data->head; + + for(i = 0; i < len && i < RARRAY_LEN(tags); i++) { + VALUE tuple = RARRAY_AREF(tags, i); + VALUE name; + VALUE value; + + Check_Type(tuple, T_ARRAY); + + if(RARRAY_LEN(tuple) < 2) { + rb_raise(rb_eRuntimeError, "tag tuple must be of length 2"); + } + + name = RARRAY_AREF(tuple, 0); + value = RARRAY_AREF(tuple, 1); + StringValue(name); + StringValue(value); + name = rb_str_export_to_enc(name, encoding); + value = rb_str_export_to_enc(value, encoding); + + tail->handle = (yaml_char_t *)StringValueCStr(name); + tail->prefix = (yaml_char_t *)StringValueCStr(value); + + tail++; + } } yaml_document_start_event_initialize( - &event, - (RARRAY_LEN(version) > 0) ? &version_directive : NULL, - head, - tail, - imp ? 1 : 0 - ); + &event, + (RARRAY_LEN(version) > 0) ? &version_directive : NULL, + data->head, + tail, + imp ? 1 : 0 + ); emit(emitter, &event); - if(head) xfree(head); - return self; } +static VALUE start_document_ensure(VALUE d) +{ + struct start_document_data * data = (struct start_document_data *)d; + + xfree(data->head); + + return Qnil; +} + +/* call-seq: emitter.start_document(version, tags, implicit) + * + * Start a document emission with YAML +version+, +tags+, and an +implicit+ + * start. + * + * See Psych::Handler#start_document + */ +static VALUE start_document(VALUE self, VALUE version, VALUE tags, VALUE imp) +{ + struct start_document_data data = { + .self = self, + .version = version, + .tags = tags, + .imp = imp, + + .head = NULL, + }; + + return rb_ensure(start_document_try, (VALUE)&data, start_document_ensure, (VALUE)&data); +} + /* call-seq: emitter.end_document(implicit) * * End a document emission with an +implicit+ ending. @@ -241,14 +275,14 @@ static VALUE end_document(VALUE self, VALUE imp) * See Psych::Handler#scalar */ static VALUE scalar( - VALUE self, - VALUE value, - VALUE anchor, - VALUE tag, - VALUE plain, - VALUE quoted, - VALUE style - ) { + VALUE self, + VALUE value, + VALUE anchor, + VALUE tag, + VALUE plain, + VALUE quoted, + VALUE style + ) { yaml_emitter_t * emitter; yaml_event_t event; rb_encoding *encoding; @@ -261,25 +295,26 @@ static VALUE scalar( value = rb_str_export_to_enc(value, encoding); if(!NIL_P(anchor)) { - Check_Type(anchor, T_STRING); - anchor = rb_str_export_to_enc(anchor, encoding); + Check_Type(anchor, T_STRING); + anchor = rb_str_export_to_enc(anchor, encoding); } if(!NIL_P(tag)) { - Check_Type(tag, T_STRING); - tag = rb_str_export_to_enc(tag, encoding); + Check_Type(tag, T_STRING); + tag = rb_str_export_to_enc(tag, encoding); } + const char *value_ptr = StringValuePtr(value); yaml_scalar_event_initialize( - &event, - (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)), - (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)), - (yaml_char_t*)StringValuePtr(value), - (int)RSTRING_LEN(value), - plain ? 1 : 0, - quoted ? 1 : 0, - (yaml_scalar_style_t)NUM2INT(style) - ); + &event, + (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)), + (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)), + (yaml_char_t*)value_ptr, + (int)RSTRING_LEN(value), + plain ? 1 : 0, + quoted ? 1 : 0, + (yaml_scalar_style_t)NUM2INT(style) + ); emit(emitter, &event); @@ -294,36 +329,36 @@ static VALUE scalar( * See Psych::Handler#start_sequence */ static VALUE start_sequence( - VALUE self, - VALUE anchor, - VALUE tag, - VALUE implicit, - VALUE style - ) { + VALUE self, + VALUE anchor, + VALUE tag, + VALUE implicit, + VALUE style + ) { yaml_emitter_t * emitter; yaml_event_t event; rb_encoding * encoding = rb_utf8_encoding(); if(!NIL_P(anchor)) { - Check_Type(anchor, T_STRING); - anchor = rb_str_export_to_enc(anchor, encoding); + Check_Type(anchor, T_STRING); + anchor = rb_str_export_to_enc(anchor, encoding); } if(!NIL_P(tag)) { - Check_Type(tag, T_STRING); - tag = rb_str_export_to_enc(tag, encoding); + Check_Type(tag, T_STRING); + tag = rb_str_export_to_enc(tag, encoding); } TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); yaml_sequence_start_event_initialize( - &event, - (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)), - (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)), - implicit ? 1 : 0, - (yaml_sequence_style_t)NUM2INT(style) - ); + &event, + (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)), + (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)), + implicit ? 1 : 0, + (yaml_sequence_style_t)NUM2INT(style) + ); emit(emitter, &event); @@ -357,12 +392,12 @@ static VALUE end_sequence(VALUE self) * See Psych::Handler#start_mapping */ static VALUE start_mapping( - VALUE self, - VALUE anchor, - VALUE tag, - VALUE implicit, - VALUE style - ) { + VALUE self, + VALUE anchor, + VALUE tag, + VALUE implicit, + VALUE style + ) { yaml_emitter_t * emitter; yaml_event_t event; rb_encoding *encoding; @@ -372,22 +407,22 @@ static VALUE start_mapping( encoding = rb_utf8_encoding(); if(!NIL_P(anchor)) { - Check_Type(anchor, T_STRING); - anchor = rb_str_export_to_enc(anchor, encoding); + Check_Type(anchor, T_STRING); + anchor = rb_str_export_to_enc(anchor, encoding); } if(!NIL_P(tag)) { - Check_Type(tag, T_STRING); - tag = rb_str_export_to_enc(tag, encoding); + Check_Type(tag, T_STRING); + tag = rb_str_export_to_enc(tag, encoding); } yaml_mapping_start_event_initialize( - &event, - (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)), - (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)), - implicit ? 1 : 0, - (yaml_mapping_style_t)NUM2INT(style) - ); + &event, + (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)), + (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)), + implicit ? 1 : 0, + (yaml_mapping_style_t)NUM2INT(style) + ); emit(emitter, &event); @@ -426,14 +461,14 @@ static VALUE alias(VALUE self, VALUE anchor) TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); if(!NIL_P(anchor)) { - Check_Type(anchor, T_STRING); - anchor = rb_str_export_to_enc(anchor, rb_utf8_encoding()); + Check_Type(anchor, T_STRING); + anchor = rb_str_export_to_enc(anchor, rb_utf8_encoding()); } yaml_alias_event_initialize( - &event, - (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)) - ); + &event, + (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)) + ); emit(emitter, &event); @@ -552,4 +587,3 @@ void Init_psych_emitter(void) id_indentation = rb_intern("indentation"); id_canonical = rb_intern("canonical"); } -/* vim: set noet sws=4 sw=4: */ diff --git a/ext/psych/psych_parser.c b/ext/psych/psych_parser.c index 9c5179cc44..d973496284 100644 --- a/ext/psych/psych_parser.c +++ b/ext/psych/psych_parser.c @@ -32,9 +32,9 @@ static int io_reader(void * data, unsigned char *buf, size_t size, size_t *read) *read = 0; if(! NIL_P(string)) { - void * str = (void *)StringValuePtr(string); - *read = (size_t)RSTRING_LEN(string); - memcpy(buf, str, *read); + void * str = (void *)StringValuePtr(string); + *read = (size_t)RSTRING_LEN(string); + memcpy(buf, str, *read); } return 1; @@ -80,23 +80,23 @@ static VALUE allocate(VALUE klass) static VALUE make_exception(yaml_parser_t * parser, VALUE path) { if (parser->error == YAML_MEMORY_ERROR) { - return rb_eNoMemError; + return rb_eNoMemError; } else { - size_t line, column; - VALUE ePsychSyntaxError; + size_t line, column; + VALUE ePsychSyntaxError; - line = parser->context_mark.line + 1; - column = parser->context_mark.column + 1; + line = parser->context_mark.line + 1; + column = parser->context_mark.column + 1; - ePsychSyntaxError = rb_const_get(mPsych, rb_intern("SyntaxError")); + ePsychSyntaxError = rb_const_get(mPsych, rb_intern("SyntaxError")); - return rb_funcall(ePsychSyntaxError, rb_intern("new"), 6, - path, - SIZET2NUM(line), - SIZET2NUM(column), - SIZET2NUM(parser->problem_offset), - parser->problem ? rb_usascii_str_new2(parser->problem) : Qnil, - parser->context ? rb_usascii_str_new2(parser->context) : Qnil); + return rb_funcall(ePsychSyntaxError, rb_intern("new"), 6, + path, + SIZET2NUM(line), + SIZET2NUM(column), + SIZET2NUM(parser->problem_offset), + parser->problem ? rb_usascii_str_new2(parser->problem) : Qnil, + parser->context ? rb_usascii_str_new2(parser->context) : Qnil); } } @@ -108,18 +108,18 @@ static VALUE transcode_string(VALUE src, int * parser_encoding) int source_encoding = rb_enc_get_index(src); if (source_encoding == utf8) { - *parser_encoding = YAML_UTF8_ENCODING; - return src; + *parser_encoding = YAML_UTF8_ENCODING; + return src; } if (source_encoding == utf16le) { - *parser_encoding = YAML_UTF16LE_ENCODING; - return src; + *parser_encoding = YAML_UTF16LE_ENCODING; + return src; } if (source_encoding == utf16be) { - *parser_encoding = YAML_UTF16BE_ENCODING; - return src; + *parser_encoding = YAML_UTF16BE_ENCODING; + return src; } src = rb_str_export_to_enc(src, rb_utf8_encoding()); @@ -138,36 +138,36 @@ static VALUE transcode_io(VALUE src, int * parser_encoding) /* if no encoding is returned, assume ascii8bit. */ if (NIL_P(io_external_encoding)) { - io_external_enc_index = rb_ascii8bit_encindex(); + io_external_enc_index = rb_ascii8bit_encindex(); } else { - io_external_enc_index = rb_to_encoding_index(io_external_encoding); + io_external_enc_index = rb_to_encoding_index(io_external_encoding); } /* Treat US-ASCII as utf_8 */ if (io_external_enc_index == rb_usascii_encindex()) { - *parser_encoding = YAML_UTF8_ENCODING; - return src; + *parser_encoding = YAML_UTF8_ENCODING; + return src; } if (io_external_enc_index == rb_utf8_encindex()) { - *parser_encoding = YAML_UTF8_ENCODING; - return src; + *parser_encoding = YAML_UTF8_ENCODING; + return src; } if (io_external_enc_index == rb_enc_find_index("UTF-16LE")) { - *parser_encoding = YAML_UTF16LE_ENCODING; - return src; + *parser_encoding = YAML_UTF16LE_ENCODING; + return src; } if (io_external_enc_index == rb_enc_find_index("UTF-16BE")) { - *parser_encoding = YAML_UTF16BE_ENCODING; - return src; + *parser_encoding = YAML_UTF16BE_ENCODING; + return src; } /* Just guess on ASCII-8BIT */ if (io_external_enc_index == rb_ascii8bit_encindex()) { - *parser_encoding = YAML_ANY_ENCODING; - return src; + *parser_encoding = YAML_ANY_ENCODING; + return src; } /* If the external encoding is something we don't know how to handle, @@ -261,238 +261,238 @@ static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path) yaml_parser_initialize(parser); if (rb_respond_to(yaml, id_read)) { - yaml = transcode_io(yaml, &parser_encoding); - yaml_parser_set_encoding(parser, parser_encoding); - yaml_parser_set_input(parser, io_reader, (void *)yaml); + yaml = transcode_io(yaml, &parser_encoding); + yaml_parser_set_encoding(parser, parser_encoding); + yaml_parser_set_input(parser, io_reader, (void *)yaml); } else { - StringValue(yaml); - yaml = transcode_string(yaml, &parser_encoding); - yaml_parser_set_encoding(parser, parser_encoding); - yaml_parser_set_input_string( - parser, - (const unsigned char *)RSTRING_PTR(yaml), - (size_t)RSTRING_LEN(yaml) - ); + StringValue(yaml); + yaml = transcode_string(yaml, &parser_encoding); + yaml_parser_set_encoding(parser, parser_encoding); + yaml_parser_set_input_string( + parser, + (const unsigned char *)RSTRING_PTR(yaml), + (size_t)RSTRING_LEN(yaml) + ); } while(!done) { - VALUE event_args[5]; - VALUE start_line, start_column, end_line, end_column; - - if(parser->error || !yaml_parser_parse(parser, &event)) { - VALUE exception; - - exception = make_exception(parser, path); - yaml_parser_delete(parser); - yaml_parser_initialize(parser); - - rb_exc_raise(exception); - } - - start_line = SIZET2NUM(event.start_mark.line); - start_column = SIZET2NUM(event.start_mark.column); - end_line = SIZET2NUM(event.end_mark.line); - end_column = SIZET2NUM(event.end_mark.column); - - event_args[0] = handler; - event_args[1] = start_line; - event_args[2] = start_column; - event_args[3] = end_line; - event_args[4] = end_column; - rb_protect(protected_event_location, (VALUE)event_args, &state); - - switch(event.type) { - case YAML_STREAM_START_EVENT: - { - VALUE args[2]; - - args[0] = handler; - args[1] = INT2NUM(event.data.stream_start.encoding); - rb_protect(protected_start_stream, (VALUE)args, &state); - } - break; - case YAML_DOCUMENT_START_EVENT: - { - VALUE args[4]; - /* Get a list of tag directives (if any) */ - VALUE tag_directives = rb_ary_new(); - /* Grab the document version */ - VALUE version = event.data.document_start.version_directive ? - rb_ary_new3( - (long)2, - INT2NUM(event.data.document_start.version_directive->major), - INT2NUM(event.data.document_start.version_directive->minor) - ) : rb_ary_new(); - - if(event.data.document_start.tag_directives.start) { - yaml_tag_directive_t *start = - event.data.document_start.tag_directives.start; - yaml_tag_directive_t *end = - event.data.document_start.tag_directives.end; - for(; start != end; start++) { - VALUE handle = Qnil; - VALUE prefix = Qnil; - if(start->handle) { - handle = rb_str_new2((const char *)start->handle); - PSYCH_TRANSCODE(handle, encoding, internal_enc); - } - - if(start->prefix) { - prefix = rb_str_new2((const char *)start->prefix); - PSYCH_TRANSCODE(prefix, encoding, internal_enc); - } - - rb_ary_push(tag_directives, rb_ary_new3((long)2, handle, prefix)); - } - } - args[0] = handler; - args[1] = version; - args[2] = tag_directives; - args[3] = event.data.document_start.implicit == 1 ? Qtrue : Qfalse; - rb_protect(protected_start_document, (VALUE)args, &state); - } - break; - case YAML_DOCUMENT_END_EVENT: - { - VALUE args[2]; - - args[0] = handler; - args[1] = event.data.document_end.implicit == 1 ? Qtrue : Qfalse; - rb_protect(protected_end_document, (VALUE)args, &state); - } - break; - case YAML_ALIAS_EVENT: - { - VALUE args[2]; - VALUE alias = Qnil; - if(event.data.alias.anchor) { - alias = rb_str_new2((const char *)event.data.alias.anchor); - PSYCH_TRANSCODE(alias, encoding, internal_enc); - } - - args[0] = handler; - args[1] = alias; - rb_protect(protected_alias, (VALUE)args, &state); - } - break; - case YAML_SCALAR_EVENT: - { - VALUE args[7]; - VALUE anchor = Qnil; - VALUE tag = Qnil; - VALUE plain_implicit, quoted_implicit, style; - VALUE val = rb_str_new( - (const char *)event.data.scalar.value, - (long)event.data.scalar.length - ); - - PSYCH_TRANSCODE(val, encoding, internal_enc); - - if(event.data.scalar.anchor) { - anchor = rb_str_new2((const char *)event.data.scalar.anchor); - PSYCH_TRANSCODE(anchor, encoding, internal_enc); - } - - if(event.data.scalar.tag) { - tag = rb_str_new2((const char *)event.data.scalar.tag); - PSYCH_TRANSCODE(tag, encoding, internal_enc); - } - - plain_implicit = - event.data.scalar.plain_implicit == 0 ? Qfalse : Qtrue; - - quoted_implicit = - event.data.scalar.quoted_implicit == 0 ? Qfalse : Qtrue; - - style = INT2NUM(event.data.scalar.style); - - args[0] = handler; - args[1] = val; - args[2] = anchor; - args[3] = tag; - args[4] = plain_implicit; - args[5] = quoted_implicit; - args[6] = style; - rb_protect(protected_scalar, (VALUE)args, &state); - } - break; - case YAML_SEQUENCE_START_EVENT: - { - VALUE args[5]; - VALUE anchor = Qnil; - VALUE tag = Qnil; - VALUE implicit, style; - if(event.data.sequence_start.anchor) { - anchor = rb_str_new2((const char *)event.data.sequence_start.anchor); - PSYCH_TRANSCODE(anchor, encoding, internal_enc); - } - - tag = Qnil; - if(event.data.sequence_start.tag) { - tag = rb_str_new2((const char *)event.data.sequence_start.tag); - PSYCH_TRANSCODE(tag, encoding, internal_enc); - } - - implicit = - event.data.sequence_start.implicit == 0 ? Qfalse : Qtrue; - - style = INT2NUM(event.data.sequence_start.style); - - args[0] = handler; - args[1] = anchor; - args[2] = tag; - args[3] = implicit; - args[4] = style; - - rb_protect(protected_start_sequence, (VALUE)args, &state); - } - break; - case YAML_SEQUENCE_END_EVENT: - rb_protect(protected_end_sequence, handler, &state); - break; - case YAML_MAPPING_START_EVENT: - { - VALUE args[5]; - VALUE anchor = Qnil; - VALUE tag = Qnil; - VALUE implicit, style; - if(event.data.mapping_start.anchor) { - anchor = rb_str_new2((const char *)event.data.mapping_start.anchor); - PSYCH_TRANSCODE(anchor, encoding, internal_enc); - } - - if(event.data.mapping_start.tag) { - tag = rb_str_new2((const char *)event.data.mapping_start.tag); - PSYCH_TRANSCODE(tag, encoding, internal_enc); - } - - implicit = - event.data.mapping_start.implicit == 0 ? Qfalse : Qtrue; - - style = INT2NUM(event.data.mapping_start.style); - - args[0] = handler; - args[1] = anchor; - args[2] = tag; - args[3] = implicit; - args[4] = style; - - rb_protect(protected_start_mapping, (VALUE)args, &state); - } - break; - case YAML_MAPPING_END_EVENT: - rb_protect(protected_end_mapping, handler, &state); - break; - case YAML_NO_EVENT: - rb_protect(protected_empty, handler, &state); - break; - case YAML_STREAM_END_EVENT: - rb_protect(protected_end_stream, handler, &state); - done = 1; - break; - } - yaml_event_delete(&event); - if (state) rb_jump_tag(state); + VALUE event_args[5]; + VALUE start_line, start_column, end_line, end_column; + + if(parser->error || !yaml_parser_parse(parser, &event)) { + VALUE exception; + + exception = make_exception(parser, path); + yaml_parser_delete(parser); + yaml_parser_initialize(parser); + + rb_exc_raise(exception); + } + + start_line = SIZET2NUM(event.start_mark.line); + start_column = SIZET2NUM(event.start_mark.column); + end_line = SIZET2NUM(event.end_mark.line); + end_column = SIZET2NUM(event.end_mark.column); + + event_args[0] = handler; + event_args[1] = start_line; + event_args[2] = start_column; + event_args[3] = end_line; + event_args[4] = end_column; + rb_protect(protected_event_location, (VALUE)event_args, &state); + + switch(event.type) { + case YAML_STREAM_START_EVENT: + { + VALUE args[2]; + + args[0] = handler; + args[1] = INT2NUM(event.data.stream_start.encoding); + rb_protect(protected_start_stream, (VALUE)args, &state); + } + break; + case YAML_DOCUMENT_START_EVENT: + { + VALUE args[4]; + /* Get a list of tag directives (if any) */ + VALUE tag_directives = rb_ary_new(); + /* Grab the document version */ + VALUE version = event.data.document_start.version_directive ? + rb_ary_new3( + (long)2, + INT2NUM(event.data.document_start.version_directive->major), + INT2NUM(event.data.document_start.version_directive->minor) + ) : rb_ary_new(); + + if(event.data.document_start.tag_directives.start) { + yaml_tag_directive_t *start = + event.data.document_start.tag_directives.start; + yaml_tag_directive_t *end = + event.data.document_start.tag_directives.end; + for(; start != end; start++) { + VALUE handle = Qnil; + VALUE prefix = Qnil; + if(start->handle) { + handle = rb_str_new2((const char *)start->handle); + PSYCH_TRANSCODE(handle, encoding, internal_enc); + } + + if(start->prefix) { + prefix = rb_str_new2((const char *)start->prefix); + PSYCH_TRANSCODE(prefix, encoding, internal_enc); + } + + rb_ary_push(tag_directives, rb_ary_new3((long)2, handle, prefix)); + } + } + args[0] = handler; + args[1] = version; + args[2] = tag_directives; + args[3] = event.data.document_start.implicit == 1 ? Qtrue : Qfalse; + rb_protect(protected_start_document, (VALUE)args, &state); + } + break; + case YAML_DOCUMENT_END_EVENT: + { + VALUE args[2]; + + args[0] = handler; + args[1] = event.data.document_end.implicit == 1 ? Qtrue : Qfalse; + rb_protect(protected_end_document, (VALUE)args, &state); + } + break; + case YAML_ALIAS_EVENT: + { + VALUE args[2]; + VALUE alias = Qnil; + if(event.data.alias.anchor) { + alias = rb_str_new2((const char *)event.data.alias.anchor); + PSYCH_TRANSCODE(alias, encoding, internal_enc); + } + + args[0] = handler; + args[1] = alias; + rb_protect(protected_alias, (VALUE)args, &state); + } + break; + case YAML_SCALAR_EVENT: + { + VALUE args[7]; + VALUE anchor = Qnil; + VALUE tag = Qnil; + VALUE plain_implicit, quoted_implicit, style; + VALUE val = rb_str_new( + (const char *)event.data.scalar.value, + (long)event.data.scalar.length + ); + + PSYCH_TRANSCODE(val, encoding, internal_enc); + + if(event.data.scalar.anchor) { + anchor = rb_str_new2((const char *)event.data.scalar.anchor); + PSYCH_TRANSCODE(anchor, encoding, internal_enc); + } + + if(event.data.scalar.tag) { + tag = rb_str_new2((const char *)event.data.scalar.tag); + PSYCH_TRANSCODE(tag, encoding, internal_enc); + } + + plain_implicit = + event.data.scalar.plain_implicit == 0 ? Qfalse : Qtrue; + + quoted_implicit = + event.data.scalar.quoted_implicit == 0 ? Qfalse : Qtrue; + + style = INT2NUM(event.data.scalar.style); + + args[0] = handler; + args[1] = val; + args[2] = anchor; + args[3] = tag; + args[4] = plain_implicit; + args[5] = quoted_implicit; + args[6] = style; + rb_protect(protected_scalar, (VALUE)args, &state); + } + break; + case YAML_SEQUENCE_START_EVENT: + { + VALUE args[5]; + VALUE anchor = Qnil; + VALUE tag = Qnil; + VALUE implicit, style; + if(event.data.sequence_start.anchor) { + anchor = rb_str_new2((const char *)event.data.sequence_start.anchor); + PSYCH_TRANSCODE(anchor, encoding, internal_enc); + } + + tag = Qnil; + if(event.data.sequence_start.tag) { + tag = rb_str_new2((const char *)event.data.sequence_start.tag); + PSYCH_TRANSCODE(tag, encoding, internal_enc); + } + + implicit = + event.data.sequence_start.implicit == 0 ? Qfalse : Qtrue; + + style = INT2NUM(event.data.sequence_start.style); + + args[0] = handler; + args[1] = anchor; + args[2] = tag; + args[3] = implicit; + args[4] = style; + + rb_protect(protected_start_sequence, (VALUE)args, &state); + } + break; + case YAML_SEQUENCE_END_EVENT: + rb_protect(protected_end_sequence, handler, &state); + break; + case YAML_MAPPING_START_EVENT: + { + VALUE args[5]; + VALUE anchor = Qnil; + VALUE tag = Qnil; + VALUE implicit, style; + if(event.data.mapping_start.anchor) { + anchor = rb_str_new2((const char *)event.data.mapping_start.anchor); + PSYCH_TRANSCODE(anchor, encoding, internal_enc); + } + + if(event.data.mapping_start.tag) { + tag = rb_str_new2((const char *)event.data.mapping_start.tag); + PSYCH_TRANSCODE(tag, encoding, internal_enc); + } + + implicit = + event.data.mapping_start.implicit == 0 ? Qfalse : Qtrue; + + style = INT2NUM(event.data.mapping_start.style); + + args[0] = handler; + args[1] = anchor; + args[2] = tag; + args[3] = implicit; + args[4] = style; + + rb_protect(protected_start_mapping, (VALUE)args, &state); + } + break; + case YAML_MAPPING_END_EVENT: + rb_protect(protected_end_mapping, handler, &state); + break; + case YAML_NO_EVENT: + rb_protect(protected_empty, handler, &state); + break; + case YAML_STREAM_END_EVENT: + rb_protect(protected_end_stream, handler, &state); + done = 1; + break; + } + yaml_event_delete(&event); + if (state) rb_jump_tag(state); } return self; @@ -562,4 +562,3 @@ void Init_psych_parser(void) id_end_mapping = rb_intern("end_mapping"); id_event_location = rb_intern("event_location"); } -/* vim: set noet sws=4 sw=4: */ diff --git a/ext/psych/psych_to_ruby.c b/ext/psych/psych_to_ruby.c index b388ff7754..3ab0138b52 100644 --- a/ext/psych/psych_to_ruby.c +++ b/ext/psych/psych_to_ruby.c @@ -10,7 +10,11 @@ static VALUE build_exception(VALUE self, VALUE klass, VALUE mesg) { VALUE e = rb_obj_alloc(klass); +#ifdef TRUFFLERUBY + rb_exc_set_message(e, mesg); +#else rb_iv_set(e, "mesg", mesg); +#endif return e; } @@ -36,4 +40,3 @@ void Init_psych_to_ruby(void) rb_define_private_method(cPsychVisitorsToRuby, "build_exception", build_exception, 2); rb_define_private_method(class_loader, "path2class", path2class, 1); } -/* vim: set noet sws=4 sw=4: */ diff --git a/ext/psych/psych_yaml_tree.c b/ext/psych/psych_yaml_tree.c index 225655d127..bbd93f874d 100644 --- a/ext/psych/psych_yaml_tree.c +++ b/ext/psych/psych_yaml_tree.c @@ -9,4 +9,3 @@ void Init_psych_yaml_tree(void) VALUE visitor = rb_define_class_under(visitors, "Visitor", rb_cObject); cPsychVisitorsYamlTree = rb_define_class_under(visitors, "YAMLTree", visitor); } -/* vim: set noet sws=4 sw=4: */ diff --git a/ext/pty/depend b/ext/pty/depend index d4d0d558ef..8fa018d084 100644 --- a/ext/pty/depend +++ b/ext/pty/depend @@ -138,6 +138,7 @@ pty.o: $(hdrdir)/ruby/internal/intern/re.h pty.o: $(hdrdir)/ruby/internal/intern/ruby.h pty.o: $(hdrdir)/ruby/internal/intern/select.h pty.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +pty.o: $(hdrdir)/ruby/internal/intern/set.h pty.o: $(hdrdir)/ruby/internal/intern/signal.h pty.o: $(hdrdir)/ruby/internal/intern/sprintf.h pty.o: $(hdrdir)/ruby/internal/intern/string.h @@ -157,6 +158,7 @@ pty.o: $(hdrdir)/ruby/internal/special_consts.h pty.o: $(hdrdir)/ruby/internal/static_assert.h pty.o: $(hdrdir)/ruby/internal/stdalign.h pty.o: $(hdrdir)/ruby/internal/stdbool.h +pty.o: $(hdrdir)/ruby/internal/stdckdint.h pty.o: $(hdrdir)/ruby/internal/symbol.h pty.o: $(hdrdir)/ruby/internal/value.h pty.o: $(hdrdir)/ruby/internal/value_type.h @@ -171,6 +173,7 @@ pty.o: $(hdrdir)/ruby/ruby.h pty.o: $(hdrdir)/ruby/st.h pty.o: $(hdrdir)/ruby/subst.h pty.o: $(hdrdir)/ruby/util.h +pty.o: $(top_srcdir)/id_table.h pty.o: $(top_srcdir)/internal.h pty.o: $(top_srcdir)/internal/array.h pty.o: $(top_srcdir)/internal/compilers.h diff --git a/ext/pty/extconf.rb b/ext/pty/extconf.rb index ba0c4286fd..da3655cf4d 100644 --- a/ext/pty/extconf.rb +++ b/ext/pty/extconf.rb @@ -13,10 +13,13 @@ if /mswin|mingw|bccwin/ !~ RUBY_PLATFORM have_header("util.h") # OpenBSD openpty util = have_library("util", "openpty") end - if have_func("posix_openpt") or + openpt = have_func("posix_openpt") + if openpt + have_func("ptsname_r") or have_func("ptsname") + end + if openpt or (util or have_func("openpty")) or have_func("_getpty") or - have_func("ptsname") or have_func("ioctl") create_makefile('pty') end diff --git a/ext/pty/pty.c b/ext/pty/pty.c index 8dca8ba281..7c79f81e33 100644 --- a/ext/pty/pty.c +++ b/ext/pty/pty.c @@ -92,9 +92,13 @@ struct pty_info { static void getDevice(int*, int*, char [DEVICELEN], int); +static int start_new_session(char *errbuf, size_t errbuf_len); +static int obtain_ctty(int master, int slave, const char *slavename, char *errbuf, size_t errbuf_len); +static int drop_privilege(char *errbuf, size_t errbuf_len); + struct child_info { int master, slave; - char *slavename; + const char *slavename; VALUE execarg_obj; struct rb_execarg *eargp; }; @@ -102,26 +106,42 @@ struct child_info { static int chfunc(void *data, char *errbuf, size_t errbuf_len) { - struct child_info *carg = data; + const struct child_info *carg = data; int master = carg->master; int slave = carg->slave; + const char *slavename = carg->slavename; + + if (start_new_session(errbuf, errbuf_len)) + return -1; + + if (obtain_ctty(master, slave, slavename, errbuf, errbuf_len)) + return -1; + + if (drop_privilege(errbuf, errbuf_len)) + return -1; + + return rb_exec_async_signal_safe(carg->eargp, errbuf, errbuf_len); +} #define ERROR_EXIT(str) do { \ strlcpy(errbuf, (str), errbuf_len); \ return -1; \ } while (0) - /* - * Set free from process group and controlling terminal - */ +/* + * Set free from process group and controlling terminal + */ +static int +start_new_session(char *errbuf, size_t errbuf_len) +{ #ifdef HAVE_SETSID (void) setsid(); #else /* HAS_SETSID */ # ifdef HAVE_SETPGRP -# ifdef SETGRP_VOID +# ifdef SETPGRP_VOID if (setpgrp() == -1) ERROR_EXIT("setpgrp()"); -# else /* SETGRP_VOID */ +# else /* SETPGRP_VOID */ if (setpgrp(0, getpid()) == -1) ERROR_EXIT("setpgrp()"); { @@ -132,20 +152,25 @@ chfunc(void *data, char *errbuf, size_t errbuf_len) ERROR_EXIT("ioctl(TIOCNOTTY)"); close(i); } -# endif /* SETGRP_VOID */ +# endif /* SETPGRP_VOID */ # endif /* HAVE_SETPGRP */ #endif /* HAS_SETSID */ + return 0; +} - /* - * obtain new controlling terminal - */ +/* + * obtain new controlling terminal + */ +static int +obtain_ctty(int master, int slave, const char *slavename, char *errbuf, size_t errbuf_len) +{ #if defined(TIOCSCTTY) close(master); (void) ioctl(slave, TIOCSCTTY, (char *)0); /* errors ignored for sun */ #else close(slave); - slave = rb_cloexec_open(carg->slavename, O_RDWR, 0); + slave = rb_cloexec_open(slavename, O_RDWR, 0); if (slave < 0) { ERROR_EXIT("open: pty slave"); } @@ -155,14 +180,20 @@ chfunc(void *data, char *errbuf, size_t errbuf_len) dup2(slave,0); dup2(slave,1); dup2(slave,2); - if (slave < 0 || slave > 2) (void)!close(slave); + if (slave > 2) (void)!close(slave); + return 0; +} + +static int +drop_privilege(char *errbuf, size_t errbuf_len) +{ #if defined(HAVE_SETEUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRESUID) if (seteuid(getuid())) ERROR_EXIT("seteuid()"); #endif + return 0; +} - return rb_exec_async_signal_safe(carg->eargp, errbuf, sizeof(errbuf_len)); #undef ERROR_EXIT -} static void establishShell(int argc, VALUE *argv, struct pty_info *info, @@ -184,9 +215,13 @@ establishShell(int argc, VALUE *argv, struct pty_info *info, else { #if defined HAVE_PWD_H const char *username = getenv("USER"); - struct passwd *pwent = getpwnam(username ? username : getlogin()); - if (pwent && pwent->pw_shell) - shellname = pwent->pw_shell; + if (username == NULL) + username = getlogin(); + if (username != NULL) { + struct passwd *pwent = getpwnam(username); + if (pwent && pwent->pw_shell) + shellname = pwent->pw_shell; + } #endif } v = rb_str_new2(shellname); @@ -225,7 +260,21 @@ establishShell(int argc, VALUE *argv, struct pty_info *info, RB_GC_GUARD(carg.execarg_obj); } -#if defined(HAVE_POSIX_OPENPT) || defined(HAVE_OPENPTY) || defined(HAVE_PTSNAME) +#if (defined(HAVE_POSIX_OPENPT) || defined(HAVE_PTSNAME)) && !defined(HAVE_PTSNAME_R) +/* glibc only, not obsolete interface on Tru64 or HP-UX */ +static int +ptsname_r(int fd, char *buf, size_t buflen) +{ + extern char *ptsname(int); + char *name = ptsname(fd); + if (!name) return -1; + strlcpy(buf, name, buflen); + return 0; +} +# define HAVE_PTSNAME_R 1 +#endif + +#if defined(HAVE_POSIX_OPENPT) || defined(HAVE_OPENPTY) || defined(HAVE_PTSNAME_R) static int no_mesg(char *slavedevice, int nomesg) { @@ -258,13 +307,19 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, /* Unix98 PTY */ int masterfd = -1, slavefd = -1; char *slavedevice; + struct sigaction dfl, old; + + dfl.sa_handler = SIG_DFL; + dfl.sa_flags = 0; + sigemptyset(&dfl.sa_mask); #if defined(__sun) || defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD_version < 902000) /* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */ /* FreeBSD 9.2 or later supports O_CLOEXEC * http://www.freebsd.org/cgi/query-pr.cgi?pr=162374 */ if ((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1) goto error; - if (rb_grantpt(masterfd) == -1) goto error; + if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error; + if (grantpt(masterfd) == -1) goto grantpt_error; rb_fd_fix_cloexec(masterfd); #else { @@ -278,10 +333,13 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, if ((masterfd = posix_openpt(flags)) == -1) goto error; } rb_fd_fix_cloexec(masterfd); - if (rb_grantpt(masterfd) == -1) goto error; + if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error; + if (grantpt(masterfd) == -1) goto grantpt_error; #endif + if (sigaction(SIGCHLD, &old, NULL) == -1) goto error; if (unlockpt(masterfd) == -1) goto error; - if ((slavedevice = ptsname(masterfd)) == NULL) goto error; + if (ptsname_r(masterfd, SlaveName, DEVICELEN) != 0) goto error; + slavedevice = SlaveName; if (no_mesg(slavedevice, nomesg) == -1) goto error; if ((slavefd = rb_cloexec_open(slavedevice, O_RDWR|O_NOCTTY, 0)) == -1) goto error; rb_update_max_fd(slavefd); @@ -294,9 +352,10 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, *master = masterfd; *slave = slavefd; - strlcpy(SlaveName, slavedevice, DEVICELEN); return 0; + grantpt_error: + sigaction(SIGCHLD, &old, NULL); error: if (slavefd != -1) close(slavefd); if (masterfd != -1) close(masterfd); @@ -346,21 +405,25 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, char *slavedevice; void (*s)(); - extern char *ptsname(int); extern int unlockpt(int); + extern int grantpt(int); #if defined(__sun) /* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */ if((masterfd = open("/dev/ptmx", O_RDWR, 0)) == -1) goto error; - if(rb_grantpt(masterfd) == -1) goto error; + s = signal(SIGCHLD, SIG_DFL); + if(grantpt(masterfd) == -1) goto error; rb_fd_fix_cloexec(masterfd); #else if((masterfd = rb_cloexec_open("/dev/ptmx", O_RDWR, 0)) == -1) goto error; rb_update_max_fd(masterfd); - if(rb_grantpt(masterfd) == -1) goto error; + s = signal(SIGCHLD, SIG_DFL); + if(grantpt(masterfd) == -1) goto error; #endif + signal(SIGCHLD, s); if(unlockpt(masterfd) == -1) goto error; - if((slavedevice = ptsname(masterfd)) == NULL) goto error; + if (ptsname_r(masterfd, SlaveName, DEVICELEN) != 0) goto error; + slavedevice = SlaveName; if (no_mesg(slavedevice, nomesg) == -1) goto error; if((slavefd = rb_cloexec_open(slavedevice, O_RDWR, 0)) == -1) goto error; rb_update_max_fd(slavefd); @@ -371,7 +434,6 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, #endif *master = masterfd; *slave = slavefd; - strlcpy(SlaveName, slavedevice, DEVICELEN); return 0; error: @@ -548,9 +610,17 @@ pty_detach_process(VALUE v) * +env+ is an optional hash that provides additional environment variables to the spawned pty. * * # sets FOO to "bar" - * PTY.spawn({"FOO"=>"bar"}, "printenv", "FOO") { |r,w,pid| p r.read } #=> "bar\r\n" + * PTY.spawn({"FOO"=>"bar"}, "printenv", "FOO") do |r, w, pid| + * p r.read #=> "bar\r\n" + * ensure + * r.close; w.close; Process.wait(pid) + * end * # unsets FOO - * PTY.spawn({"FOO"=>nil}, "printenv", "FOO") { |r,w,pid| p r.read } #=> "" + * PTY.spawn({"FOO"=>nil}, "printenv", "FOO") do |r, w, pid| + * p r.read #=> "" + * ensure + * r.close; w.close; Process.wait(pid) + * end * * +command+ and +command_line+ are the full commands to run, given a String. * Any additional +arguments+ will be passed to the command. @@ -566,6 +636,15 @@ pty_detach_process(VALUE v) * standard output and standard error * +w+:: A writable IO that is the command's standard input * +pid+:: The process identifier for the command. + * + * === Clean up + * + * This method does not clean up like closing IOs or waiting for child + * process, except that the process is detached in the block form to + * prevent it from becoming a zombie (see Process.detach). Any other + * cleanup is the responsibility of the caller. If waiting for +pid+, + * be sure to close both +r+ and +w+ before doing so; doing it in the + * reverse order may cause deadlock on some OSes. */ static VALUE pty_getpty(int argc, VALUE *argv, VALUE self) diff --git a/ext/rbconfig/sizeof/depend b/ext/rbconfig/sizeof/depend index 4e4ebd4ae5..7e6c950769 100644 --- a/ext/rbconfig/sizeof/depend +++ b/ext/rbconfig/sizeof/depend @@ -142,6 +142,7 @@ limits.o: $(hdrdir)/ruby/internal/intern/re.h limits.o: $(hdrdir)/ruby/internal/intern/ruby.h limits.o: $(hdrdir)/ruby/internal/intern/select.h limits.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +limits.o: $(hdrdir)/ruby/internal/intern/set.h limits.o: $(hdrdir)/ruby/internal/intern/signal.h limits.o: $(hdrdir)/ruby/internal/intern/sprintf.h limits.o: $(hdrdir)/ruby/internal/intern/string.h @@ -161,6 +162,7 @@ limits.o: $(hdrdir)/ruby/internal/special_consts.h limits.o: $(hdrdir)/ruby/internal/static_assert.h limits.o: $(hdrdir)/ruby/internal/stdalign.h limits.o: $(hdrdir)/ruby/internal/stdbool.h +limits.o: $(hdrdir)/ruby/internal/stdckdint.h limits.o: $(hdrdir)/ruby/internal/symbol.h limits.o: $(hdrdir)/ruby/internal/value.h limits.o: $(hdrdir)/ruby/internal/value_type.h @@ -300,6 +302,7 @@ sizes.o: $(hdrdir)/ruby/internal/intern/re.h sizes.o: $(hdrdir)/ruby/internal/intern/ruby.h sizes.o: $(hdrdir)/ruby/internal/intern/select.h sizes.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +sizes.o: $(hdrdir)/ruby/internal/intern/set.h sizes.o: $(hdrdir)/ruby/internal/intern/signal.h sizes.o: $(hdrdir)/ruby/internal/intern/sprintf.h sizes.o: $(hdrdir)/ruby/internal/intern/string.h @@ -319,6 +322,7 @@ sizes.o: $(hdrdir)/ruby/internal/special_consts.h sizes.o: $(hdrdir)/ruby/internal/static_assert.h sizes.o: $(hdrdir)/ruby/internal/stdalign.h sizes.o: $(hdrdir)/ruby/internal/stdbool.h +sizes.o: $(hdrdir)/ruby/internal/stdckdint.h sizes.o: $(hdrdir)/ruby/internal/symbol.h sizes.o: $(hdrdir)/ruby/internal/value.h sizes.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/ripper/depend b/ext/ripper/depend index 3b9b890de8..944da25ee9 100644 --- a/ext/ripper/depend +++ b/ext/ripper/depend @@ -1,7 +1,7 @@ GEN = $(srcdir)/tools/generate.rb SRC1 = $(top_srcdir)/parse.y SRC2 = $(srcdir)/eventids2.c -BISON = $(BASERUBY) $(top_srcdir)/tool/lrama/exe/lrama +LRAMA = $(BASERUBY) $(top_srcdir)/tool/lrama/exe/lrama .SUFFIXES: .y @@ -12,7 +12,7 @@ ripper.o: ripper.c .y.c: $(ECHO) compiling compiler $< - $(Q) $(BISON) -t -v -o$@ - $< < $< + $(Q) $(LRAMA) -o$@ - $< < $< all: check static: check @@ -181,6 +181,7 @@ eventids1.o: $(hdrdir)/ruby/internal/intern/re.h eventids1.o: $(hdrdir)/ruby/internal/intern/ruby.h eventids1.o: $(hdrdir)/ruby/internal/intern/select.h eventids1.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +eventids1.o: $(hdrdir)/ruby/internal/intern/set.h eventids1.o: $(hdrdir)/ruby/internal/intern/signal.h eventids1.o: $(hdrdir)/ruby/internal/intern/sprintf.h eventids1.o: $(hdrdir)/ruby/internal/intern/string.h @@ -200,6 +201,7 @@ eventids1.o: $(hdrdir)/ruby/internal/special_consts.h eventids1.o: $(hdrdir)/ruby/internal/static_assert.h eventids1.o: $(hdrdir)/ruby/internal/stdalign.h eventids1.o: $(hdrdir)/ruby/internal/stdbool.h +eventids1.o: $(hdrdir)/ruby/internal/stdckdint.h eventids1.o: $(hdrdir)/ruby/internal/symbol.h eventids1.o: $(hdrdir)/ruby/internal/value.h eventids1.o: $(hdrdir)/ruby/internal/value_type.h @@ -351,6 +353,7 @@ eventids2.o: $(hdrdir)/ruby/internal/intern/re.h eventids2.o: $(hdrdir)/ruby/internal/intern/ruby.h eventids2.o: $(hdrdir)/ruby/internal/intern/select.h eventids2.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +eventids2.o: $(hdrdir)/ruby/internal/intern/set.h eventids2.o: $(hdrdir)/ruby/internal/intern/signal.h eventids2.o: $(hdrdir)/ruby/internal/intern/sprintf.h eventids2.o: $(hdrdir)/ruby/internal/intern/string.h @@ -370,6 +373,7 @@ eventids2.o: $(hdrdir)/ruby/internal/special_consts.h eventids2.o: $(hdrdir)/ruby/internal/static_assert.h eventids2.o: $(hdrdir)/ruby/internal/stdalign.h eventids2.o: $(hdrdir)/ruby/internal/stdbool.h +eventids2.o: $(hdrdir)/ruby/internal/stdckdint.h eventids2.o: $(hdrdir)/ruby/internal/symbol.h eventids2.o: $(hdrdir)/ruby/internal/value.h eventids2.o: $(hdrdir)/ruby/internal/value_type.h @@ -530,6 +534,7 @@ ripper.o: $(hdrdir)/ruby/internal/intern/re.h ripper.o: $(hdrdir)/ruby/internal/intern/ruby.h ripper.o: $(hdrdir)/ruby/internal/intern/select.h ripper.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ripper.o: $(hdrdir)/ruby/internal/intern/set.h ripper.o: $(hdrdir)/ruby/internal/intern/signal.h ripper.o: $(hdrdir)/ruby/internal/intern/sprintf.h ripper.o: $(hdrdir)/ruby/internal/intern/string.h @@ -549,6 +554,7 @@ ripper.o: $(hdrdir)/ruby/internal/special_consts.h ripper.o: $(hdrdir)/ruby/internal/static_assert.h ripper.o: $(hdrdir)/ruby/internal/stdalign.h ripper.o: $(hdrdir)/ruby/internal/stdbool.h +ripper.o: $(hdrdir)/ruby/internal/stdckdint.h ripper.o: $(hdrdir)/ruby/internal/symbol.h ripper.o: $(hdrdir)/ruby/internal/value.h ripper.o: $(hdrdir)/ruby/internal/value_type.h @@ -572,12 +578,14 @@ ripper.o: $(top_srcdir)/ccan/container_of/container_of.h ripper.o: $(top_srcdir)/ccan/list/list.h ripper.o: $(top_srcdir)/ccan/str/str.h ripper.o: $(top_srcdir)/constant.h +ripper.o: $(top_srcdir)/encindex.h ripper.o: $(top_srcdir)/id_table.h ripper.o: $(top_srcdir)/internal.h ripper.o: $(top_srcdir)/internal/array.h ripper.o: $(top_srcdir)/internal/basic_operators.h ripper.o: $(top_srcdir)/internal/bignum.h ripper.o: $(top_srcdir)/internal/bits.h +ripper.o: $(top_srcdir)/internal/box.h ripper.o: $(top_srcdir)/internal/compile.h ripper.o: $(top_srcdir)/internal/compilers.h ripper.o: $(top_srcdir)/internal/complex.h @@ -595,8 +603,10 @@ ripper.o: $(top_srcdir)/internal/re.h ripper.o: $(top_srcdir)/internal/ruby_parser.h ripper.o: $(top_srcdir)/internal/sanitizers.h ripper.o: $(top_srcdir)/internal/serial.h +ripper.o: $(top_srcdir)/internal/set_table.h ripper.o: $(top_srcdir)/internal/static_assert.h ripper.o: $(top_srcdir)/internal/string.h +ripper.o: $(top_srcdir)/internal/struct.h ripper.o: $(top_srcdir)/internal/symbol.h ripper.o: $(top_srcdir)/internal/thread.h ripper.o: $(top_srcdir)/internal/variable.h @@ -767,6 +777,7 @@ ripper_init.o: $(hdrdir)/ruby/internal/intern/re.h ripper_init.o: $(hdrdir)/ruby/internal/intern/ruby.h ripper_init.o: $(hdrdir)/ruby/internal/intern/select.h ripper_init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ripper_init.o: $(hdrdir)/ruby/internal/intern/set.h ripper_init.o: $(hdrdir)/ruby/internal/intern/signal.h ripper_init.o: $(hdrdir)/ruby/internal/intern/sprintf.h ripper_init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -786,6 +797,7 @@ ripper_init.o: $(hdrdir)/ruby/internal/special_consts.h ripper_init.o: $(hdrdir)/ruby/internal/static_assert.h ripper_init.o: $(hdrdir)/ruby/internal/stdalign.h ripper_init.o: $(hdrdir)/ruby/internal/stdbool.h +ripper_init.o: $(hdrdir)/ruby/internal/stdckdint.h ripper_init.o: $(hdrdir)/ruby/internal/symbol.h ripper_init.o: $(hdrdir)/ruby/internal/value.h ripper_init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/ripper/eventids2.c b/ext/ripper/eventids2.c index 439663f0fd..87f2f588ec 100644 --- a/ext/ripper/eventids2.c +++ b/ext/ripper/eventids2.c @@ -255,7 +255,6 @@ ripper_token2eventid(enum yytokentype tok) [tRATIONAL] = O(rational), [tREGEXP_BEG] = O(regexp_beg), [tREGEXP_END] = O(regexp_end), - [tRPAREN] = O(rparen), [tRSHFT] = O(op), [tSTAR] = O(op), [tDSTAR] = O(op), diff --git a/ext/ripper/lib/ripper/lexer.rb b/ext/ripper/lib/ripper/lexer.rb index 6a3c04af30..9b849dfeae 100644 --- a/ext/ripper/lib/ripper/lexer.rb +++ b/ext/ripper/lib/ripper/lexer.rb @@ -53,6 +53,7 @@ class Ripper end class Lexer < ::Ripper #:nodoc: internal use only + # :stopdoc: class State attr_reader :to_int, :to_s @@ -67,7 +68,7 @@ class Ripper when 0, :to_int @to_int when 1, :to_s - @event + @to_s else nil end @@ -242,7 +243,12 @@ class Ripper end def on_error2(mesg, elem) - @errors.push Elem.new(elem.pos, __callee__, elem.tok, elem.state, mesg) + if elem + elem = Elem.new(elem.pos, __callee__, elem.tok, elem.state, mesg) + else + elem = Elem.new([lineno(), column()], __callee__, token(), state(), mesg) + end + @errors.push elem end PARSER_EVENTS.grep(/_error\z/) do |e| arity = PARSER_EVENT_TABLE.fetch(e) @@ -253,6 +259,7 @@ class Ripper (SCANNER_EVENTS.map {|event|:"on_#{event}"} - private_instance_methods(false)).each do |event| alias_method event, :_push_token end + # :startdoc: end # [EXPERIMENTAL] diff --git a/ext/ripper/ripper_init.c.tmpl b/ext/ripper/ripper_init.c.tmpl index a2a5e4b4cf..11e432423d 100644 --- a/ext/ripper/ripper_init.c.tmpl +++ b/ext/ripper/ripper_init.c.tmpl @@ -2,14 +2,13 @@ #include "ruby/ruby.h" #include "ruby/encoding.h" #include "internal.h" -#include "internal/imemo.h" /* needed by ruby_parser.h */ +#include "rubyparser.h" +#define YYSTYPE_IS_DECLARED +#include "parse.h" #include "internal/parse.h" #include "internal/ruby_parser.h" #include "node.h" -#include "rubyparser.h" #include "eventids1.h" -#define YYSTYPE_IS_DECLARED -#include "parse.h" #include "eventids2.h" #include "ripper_init.h" @@ -18,15 +17,40 @@ ID id_warn, id_warning, id_gets, id_assoc; +enum lex_type { + lex_type_str, + lex_type_io, + lex_type_generic, +}; + struct ripper { rb_parser_t *p; + enum lex_type type; + union { + struct lex_pointer_string ptr_str; + VALUE val; + } data; }; static void ripper_parser_mark2(void *ptr) { struct ripper *r = (struct ripper*)ptr; - if (r->p) ripper_parser_mark(r->p); + if (r->p) { + ripper_parser_mark(r->p); + + switch (r->type) { + case lex_type_str: + rb_gc_mark(r->data.ptr_str.str); + break; + case lex_type_io: + rb_gc_mark(r->data.val); + break; + case lex_type_generic: + rb_gc_mark(r->data.val); + break; + } + } } static void @@ -54,37 +78,18 @@ static const rb_data_type_t parser_data_type = { 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; -ID -ripper_get_id(VALUE v) -{ - NODE *nd; - if (!RB_TYPE_P(v, T_NODE)) return 0; - nd = (NODE *)v; - if (!nd_type_p(nd, NODE_RIPPER)) return 0; - return RNODE_RIPPER(nd)->nd_vid; -} - -VALUE -ripper_get_value(VALUE v) -{ - NODE *nd; - if (UNDEF_P(v)) return Qnil; - if (!RB_TYPE_P(v, T_NODE)) return v; - nd = (NODE *)v; - if (!nd_type_p(nd, NODE_RIPPER)) return Qnil; - return RNODE_RIPPER(nd)->nd_rval; -} - -static VALUE -ripper_lex_get_generic(struct parser_params *p, VALUE src) +static rb_parser_string_t * +ripper_lex_get_generic(struct parser_params *p, rb_parser_input_data input, int line_count) { + VALUE src = (VALUE)input; VALUE line = rb_funcallv_public(src, id_gets, 0, 0); - if (!NIL_P(line) && !RB_TYPE_P(line, T_STRING)) { + if (NIL_P(line)) return 0; + if (!RB_TYPE_P(line, T_STRING)) { rb_raise(rb_eTypeError, "gets returned %"PRIsVALUE" (expected String or nil)", rb_obj_class(line)); } - return line; + return rb_str_to_parser_string(p, line); } void @@ -100,10 +105,19 @@ ripper_compile_error(struct parser_params *p, const char *fmt, ...) ripper_error(p); } -static VALUE -ripper_lex_io_get(struct parser_params *p, VALUE src) +static rb_parser_string_t * +ripper_lex_io_get(struct parser_params *p, rb_parser_input_data input, int line_count) +{ + VALUE src = (VALUE)input; + VALUE line = rb_io_gets(src); + if (NIL_P(line)) return 0; + return rb_str_to_parser_string(p, line); +} + +static rb_parser_string_t * +ripper_lex_get_str(struct parser_params *p, rb_parser_input_data input, int line_count) { - return rb_io_gets(src); + return rb_parser_lex_get_str(p, (struct lex_pointer_string *)input); } static VALUE @@ -115,7 +129,8 @@ ripper_s_allocate(VALUE klass) &parser_data_type, r); #ifdef UNIVERSAL_PARSER - r->p = rb_parser_params_allocate(); + const rb_parser_config_t *config = rb_ruby_parser_config(); + r->p = rb_ripper_parser_params_allocate(config); #else r->p = rb_ruby_ripper_parser_allocate(); #endif @@ -176,7 +191,7 @@ ripper_parser_encoding(VALUE vparser) { struct parser_params *p = ripper_parser_params(vparser, false); - return rb_ruby_parser_encoding(p); + return rb_enc_from_encoding(rb_ruby_parser_encoding(p)); } /* @@ -237,6 +252,18 @@ ripper_parser_set_debug_output(VALUE self, VALUE output) return output; } +static int +ripper_parser_dedent_string(struct parser_params *p, VALUE string, int width) +{ + int col; + rb_parser_string_t *str; + str = rb_str_to_parser_string(p, string); + col = rb_ruby_ripper_dedent_string(p, str, width); + rb_str_replace(string, rb_str_new_parser_string(str)); + rb_parser_string_free(p, str); + return col; +} + #ifdef UNIVERSAL_PARSER struct dedent_string_arg { struct parser_params *p; @@ -252,7 +279,7 @@ parser_dedent_string0(VALUE a) StringValue(arg->input); wid = NUM2UINT(arg->width); - col = rb_ruby_ripper_dedent_string(arg->p, arg->input, wid); + col = ripper_parser_dedent_string(arg->p, arg->input, wid); return INT2NUM(col); } @@ -297,7 +324,7 @@ parser_dedent_string(VALUE self, VALUE input, VALUE width) StringValue(input); wid = NUM2UINT(width); - col = rb_ruby_ripper_dedent_string(0, input, wid); + col = ripper_parser_dedent_string(0, input, wid); return INT2NUM(col); } #endif @@ -315,26 +342,38 @@ parser_dedent_string(VALUE self, VALUE input, VALUE width) static VALUE ripper_initialize(int argc, VALUE *argv, VALUE self) { + struct ripper *r; struct parser_params *p; VALUE src, fname, lineno; - VALUE (*gets)(struct parser_params*,VALUE); - VALUE input, sourcefile_string; + rb_parser_lex_gets_func *gets; + VALUE sourcefile_string; const char *sourcefile; int sourceline; + rb_parser_input_data input; p = ripper_parser_params(self, false); + TypedData_Get_Struct(self, struct ripper, &parser_data_type, r); rb_scan_args(argc, argv, "12", &src, &fname, &lineno); if (RB_TYPE_P(src, T_FILE)) { gets = ripper_lex_io_get; + r->type = lex_type_io; + r->data.val = src; + input = (rb_parser_input_data)src; } else if (rb_respond_to(src, id_gets)) { gets = ripper_lex_get_generic; + r->type = lex_type_generic; + r->data.val = src; + input = (rb_parser_input_data)src; } else { StringValue(src); - gets = rb_ruby_ripper_lex_get_str; + gets = ripper_lex_get_str; + r->type = lex_type_str; + r->data.ptr_str.str = src; + r->data.ptr_str.ptr = 0; + input = (rb_parser_input_data)&r->data.ptr_str; } - input = src; if (NIL_P(fname)) { fname = STR_NEW2("(ripper)"); OBJ_FREEZE(fname); @@ -498,6 +537,37 @@ ripper_raw_value(VALUE self, VALUE obj) { return ULONG2NUM(obj); } + +/* :nodoc: */ +static VALUE +ripper_validate_object(VALUE self, VALUE x) +{ + if (x == Qfalse) return x; + if (x == Qtrue) return x; + if (NIL_P(x)) return x; + if (UNDEF_P(x)) + rb_raise(rb_eArgError, "Qundef given"); + if (FIXNUM_P(x)) return x; + if (SYMBOL_P(x)) return x; + switch (BUILTIN_TYPE(x)) { + case T_STRING: + case T_OBJECT: + case T_ARRAY: + case T_BIGNUM: + case T_FLOAT: + case T_COMPLEX: + case T_RATIONAL: + break; + default: + rb_raise(rb_eArgError, "wrong type of ruby object: %p (%s)", + (void *)x, rb_obj_classname(x)); + } + if (!RBASIC_CLASS(x)) { + rb_raise(rb_eArgError, "hidden ruby object: %p (%s)", + (void *)x, rb_builtin_type_name(TYPE(x))); + } + return x; +} #endif #ifdef UNIVERSAL_PARSER @@ -607,5 +677,4 @@ InitVM_ripper(void) */ rb_define_global_const("SCRIPT_LINES__", Qnil); #endif - } diff --git a/ext/ripper/ripper_init.h b/ext/ripper/ripper_init.h index 82ff13b95f..9d228107d1 100644 --- a/ext/ripper/ripper_init.h +++ b/ext/ripper/ripper_init.h @@ -1,8 +1,6 @@ #ifndef RIPPER_INIT_H #define RIPPER_INIT_H -VALUE ripper_get_value(VALUE v); -ID ripper_get_id(VALUE v); PRINTF_ARGS(void ripper_compile_error(struct parser_params*, const char *fmt, ...), 2, 3); #endif /* RIPPER_INIT_H */ diff --git a/ext/ripper/tools/dsl.rb b/ext/ripper/tools/dsl.rb index 6d662b3fb8..38f859dd97 100644 --- a/ext/ripper/tools/dsl.rb +++ b/ext/ripper/tools/dsl.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + # Simple DSL implementation for Ripper code generation # -# input: /*% ripper: stmts_add(stmts_new, void_stmt) %*/ +# input: /*% ripper: stmts_add!(stmts_new!, void_stmt!) %*/ # output: # VALUE v1, v2; # v1 = dispatch0(stmts_new); @@ -18,69 +20,158 @@ class DSL NAME_PATTERN = /(?>\$|\d+|[a-zA-Z_][a-zA-Z0-9_]*|\[[a-zA-Z_.][-a-zA-Z0-9_.]*\])(?>(?:\.|->)[a-zA-Z_][a-zA-Z0-9_]*)*/.source NOT_REF_PATTERN = /(?>\#.*|[^\"$@]*|"(?>\\.|[^\"])*")/.source - def initialize(code, options) + def self.line?(line, lineno = nil, indent: nil) + if %r<(?<space>\s*)/\*% *ripper(?:\[(?<option>.*?)\])?: *(?<code>.*?) *%\*/> =~ line + new(code, comma_split(option), lineno, indent: indent || space) + end + end + + def self.comma_split(str) + str or return [] + str.scan(/(([^(,)]+|\((?:,|\g<0>)*\))+)/).map(&:first) + end + + using Module.new { + refine Array do + def to_s + if empty? + "rb_ary_new()" + else + "rb_ary_new_from_args(#{size}, #{map(&:to_s).join(', ')})" + end + end + end + } + + class Var + class Table < Hash + def initialize(&block) + super() {|tbl, arg| + tbl.fetch(arg, &block) + } + end + + def fetch(arg, &block) + super { + self[arg] = Var.new(self, arg, &block) + } + end + + def add(&block) + v = new_var + self[v] = Var.new(self, v, &block) + end + + def defined?(name) + name = name.to_s + any? {|_, v| v.var == name} + end + + def new_var + "v#{size+1}" + end + end + + attr_reader :var, :value + + PRETTY_PRINT_INSTANCE_VARIABLES = instance_methods(false).freeze + + def pretty_print_instance_variables + PRETTY_PRINT_INSTANCE_VARIABLES + end + + alias to_s var + + def initialize(table, arg, &block) + @var = table.new_var + @value = yield arg + @table = table + end + + # Indexing. + # + # $:1 -> v1=get_value($:1) + # $:1[0] -> rb_ary_entry(v1, 0) + # $:1[0..1] -> [rb_ary_entry(v1, 0), rb_ary_entry(v1, 1)] + # *$:1[0..1] -> rb_ary_entry(v1, 0), rb_ary_entry(v1, 1) + # + # Splat needs `[range]` because `Var` does not have the length info. + def [](idx) + if ::Range === idx + idx.map {|i| self[i]} + else + @table.fetch("#@var[#{idx}]") {"rb_ary_entry(#{@var}, #{idx})"} + end + end + end + + def initialize(code, options, lineno = nil, indent: "\t\t\t") + @lineno = lineno + @indent = indent @events = {} @error = options.include?("error") - @brace = options.include?("brace") if options.include?("final") @final = "p->result" else - @final = (options.grep(/\A\$#{NAME_PATTERN}\z/o)[0] || "$$") + @final = (options.grep(/\A\$#{NAME_PATTERN}\z/o)[0] || "p->s_lvalue") end - @vars = 0 - # struct parser_params *p - p = p = "p" + bind = dsl_binding + @var_table = Var::Table.new {|arg| "get_value(#{arg})"} + code = code.gsub(%r[\G#{NOT_REF_PATTERN}\K(\$|\$:|@)#{TAG_PATTERN}?#{NAME_PATTERN}]o) { + if (arg = $&) == "$:$" + '"p->s_lvalue"' + elsif arg.start_with?("$:") + "(#{@var_table[arg]}=@var_table[#{arg.dump}])" + else + arg.dump + end + } + @last_value = bind.eval(code) + rescue SyntaxError + $stderr.puts "error on line #{@lineno}" if @lineno + raise + end - @code = "" - code = code.gsub(%r[\G#{NOT_REF_PATTERN}\K[$@]#{TAG_PATTERN}?#{NAME_PATTERN}]o, '"\&"') - @last_value = eval(code) + def dsl_binding(p = "p") + # struct parser_params *p + binding end attr_reader :events undef lambda undef hash - undef class + undef :class def generate - s = "#@code#@final=#@last_value;" - s = "{VALUE #{ (1..@vars).map {|v| "v#{ v }" }.join(",") };#{ s }}" if @vars > 0 + s = "#@final=#@last_value;" s << "ripper_error(p);" if @error - s = "{#{ s }}" if @brace - "\t\t\t#{s}" - end - - def new_var - "v#{ @vars += 1 }" - end - - def opt_event(event, default, addend) - add_event(event, [default, addend], true) + unless @var_table.empty? + vars = @var_table.map {|_, v| "#{v.var}=#{v.value}"}.join(", ") + s = "VALUE #{ vars }; #{ s }" + end + "#{@indent}{#{s}}" end - def add_event(event, args, qundef_check = false) + def add_event(event, args) event = event.to_s.sub(/!\z/, "") @events[event] = args.size vars = [] args.each do |arg| - vars << v = new_var - @code << "#{ v }=#{ arg };" + arg = @var_table.add {arg} unless Var === arg + vars << arg end - v = new_var - d = "dispatch#{ args.size }(#{ [event, *vars].join(",") })" - d = "#{ vars.last }==Qundef ? #{ vars.first } : #{ d }" if qundef_check - @code << "#{ v }=#{ d };" - v + @var_table.add {"dispatch#{ args.size }(#{ [event, *vars].join(",") })"} end def method_missing(event, *args) if event.to_s =~ /!\z/ add_event(event, args) - elsif args.empty? and /\Aid[A-Z_]/ =~ event.to_s + elsif args.empty? and (/\Aid[A-Z_]/ =~ event or @var_table.defined?(event)) event else - "#{ event }(#{ args.join(", ") })" + "#{ event }(#{ args.map(&:to_s).join(", ") })" end end @@ -88,4 +179,3 @@ class DSL name end end - diff --git a/ext/ripper/tools/generate.rb b/ext/ripper/tools/generate.rb index c44b4ba325..57ecac0b39 100644 --- a/ext/ripper/tools/generate.rb +++ b/ext/ripper/tools/generate.rb @@ -75,6 +75,7 @@ def generate_eventids1_h(ids) buf << %Q[#ifndef RIPPER_EVENTIDS1\n] buf << %Q[#define RIPPER_EVENTIDS1\n] buf << %Q[\n] + buf << %Q[#define RIPPER_ID(n) ripper_parser_ids.id_ ## n\n] buf << %Q[void ripper_init_eventids1(void);\n] buf << %Q[void ripper_init_eventids1_table(VALUE self);\n] buf << %Q[\n] @@ -84,9 +85,6 @@ def generate_eventids1_h(ids) end buf << %Q[};\n] buf << %Q[\n] - ids.each do |id, arity| - buf << %Q[#define ripper_id_#{id} ripper_parser_ids.id_#{id}\n] - end buf << %Q[#endif /* RIPPER_EVENTIDS1 */\n] buf << %Q[\n] end @@ -101,7 +99,7 @@ def generate_eventids1(ids) buf << %Q[void\n] buf << %Q[ripper_init_eventids1(void)\n] buf << %Q[{\n] - buf << %Q[#define set_id1(name) ripper_id_##name = rb_intern_const("on_"#name)\n] + buf << %Q[#define set_id1(name) RIPPER_ID(name) = rb_intern_const("on_"#name)\n] ids.each do |id, arity| buf << %Q[ set_id1(#{id});\n] end @@ -173,9 +171,7 @@ def read_ids1_with_locations(path) line.scan(/\bdispatch(\d)\((\w+)/) do |arity, event| (h[event] ||= []).push [f.lineno, arity.to_i] end - if line =~ %r</\*% *ripper(?:\[(.*?)\])?: *(.*?) *%\*/> - gen = DSL.new($2, ($1 || "").split(",")) - gen.generate + if gen = DSL.line?(line, f.lineno) gen.events.each do |event, arity| (h[event] ||= []).push [f.lineno, arity.to_i] end diff --git a/ext/ripper/tools/preproc.rb b/ext/ripper/tools/preproc.rb index a0d5e79d7d..5e8a6e0cb5 100644 --- a/ext/ripper/tools/preproc.rb +++ b/ext/ripper/tools/preproc.rb @@ -51,27 +51,26 @@ def process(f, out, path, template) usercode f, out, path, template end -def prelude(f, out) - @exprs = {} - lex_state_def = false +require_relative 'dsl' + +def generate_line(f, out) while line = f.gets - case line - when /\A%%/ + case + when gen = DSL.line?(line, f.lineno) + out << gen.generate << "\n" + when line.start_with?("%%") out << "%%\n" - return - when /\A%token/, /\A%type/, /\A} <node(?>_\w+)?>/ - # types in %union which have corresponding set_yylval_* macro. - out << line.sub(/<(?:node(?>_\w+)?|num|id)>/, '<val>') - when /^enum lex_state_(?:bits|e) \{/ - lex_state_def = true - out << line - when /^\}/ - lex_state_def = false - out << line + break else - out << line + out << yield(line) end - if lex_state_def + end +end + +def prelude(f, out) + @exprs = {} + generate_line(f, out) do |line| + if (/^enum lex_state_(?:bits|e) \{/ =~ line)..(/^\}/ =~ line) case line when /^\s*(EXPR_\w+),\s+\/\*(.+)\*\// @exprs[$1.chomp("_bit")] = $2.strip @@ -81,27 +80,21 @@ def prelude(f, out) @exprs[name] = "equals to " + (val.start_with?("(") ? "<tt>#{val}</tt>" : "+#{val}+") end end + line end end -require_relative "dsl" - def grammar(f, out) - while line = f.gets + generate_line(f, out) do |line| case line - when %r</\*% *ripper(?:\[(.*?)\])?: *(.*?) *%\*/> - out << DSL.new($2, ($1 || "").split(",")).generate << "\n" when %r</\*%%%\*/> - out << "#if 0\n" + "#if 0\n" when %r</\*%> - out << "#endif\n" + "#endif\n" when %r<%\*/> - out << "\n" - when /\A%%/ - out << "%%\n" - return + "\n" else - out << line + line end end end diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c index 6ef040b692..f1e9e42524 100644 --- a/ext/socket/ancdata.c +++ b/ext/socket/ancdata.c @@ -1643,14 +1643,14 @@ bsock_recvmsg_internal(VALUE sock, rb_obj_reveal(dat_str, rb_cString); } - ret = rb_ary_new3(3, dat_str, - rsock_io_socket_addrinfo(sock, mh.msg_name, mh.msg_namelen), #if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) - INT2NUM(mh.msg_flags) + VALUE msg_flags = INT2NUM(mh.msg_flags); #else - Qnil + VALUE msg_flags = Qnil; #endif - ); + ret = rb_ary_new3(3, dat_str, + rsock_io_socket_addrinfo(sock, mh.msg_name, mh.msg_namelen), + msg_flags); #if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) family = rsock_getfamily(fptr); diff --git a/ext/socket/basicsocket.c b/ext/socket/basicsocket.c index 54c369f6fc..2fcae8eb54 100644 --- a/ext/socket/basicsocket.c +++ b/ext/socket/basicsocket.c @@ -124,7 +124,7 @@ bsock_close_read(VALUE sock) rb_io_t *fptr; GetOpenFile(sock, fptr); - shutdown(fptr->fd, 0); + shutdown(fptr->fd, SHUT_RD); if (!(fptr->mode & FMODE_WRITABLE)) { return rb_io_close(sock); } @@ -157,7 +157,7 @@ bsock_close_write(VALUE sock) if (!(fptr->mode & FMODE_READABLE)) { return rb_io_close(sock); } - shutdown(fptr->fd, 1); + shutdown(fptr->fd, SHUT_WR); fptr->mode &= ~FMODE_WRITABLE; return Qnil; @@ -597,7 +597,7 @@ rsock_bsock_send(int argc, VALUE *argv, VALUE socket) rb_io_wait(socket, RB_INT2NUM(RUBY_IO_WRITABLE), Qnil); #endif - ssize_t n = (ssize_t)BLOCKING_REGION_FD(func, &arg); + ssize_t n = (ssize_t)rb_io_blocking_region(fptr, func, &arg); if (n >= 0) return SSIZET2NUM(n); diff --git a/ext/socket/depend b/ext/socket/depend index e95555ea92..77f6239a3d 100644 --- a/ext/socket/depend +++ b/ext/socket/depend @@ -151,6 +151,7 @@ ancdata.o: $(hdrdir)/ruby/internal/intern/re.h ancdata.o: $(hdrdir)/ruby/internal/intern/ruby.h ancdata.o: $(hdrdir)/ruby/internal/intern/select.h ancdata.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ancdata.o: $(hdrdir)/ruby/internal/intern/set.h ancdata.o: $(hdrdir)/ruby/internal/intern/signal.h ancdata.o: $(hdrdir)/ruby/internal/intern/sprintf.h ancdata.o: $(hdrdir)/ruby/internal/intern/string.h @@ -170,6 +171,7 @@ ancdata.o: $(hdrdir)/ruby/internal/special_consts.h ancdata.o: $(hdrdir)/ruby/internal/static_assert.h ancdata.o: $(hdrdir)/ruby/internal/stdalign.h ancdata.o: $(hdrdir)/ruby/internal/stdbool.h +ancdata.o: $(hdrdir)/ruby/internal/stdckdint.h ancdata.o: $(hdrdir)/ruby/internal/symbol.h ancdata.o: $(hdrdir)/ruby/internal/value.h ancdata.o: $(hdrdir)/ruby/internal/value_type.h @@ -191,9 +193,12 @@ ancdata.o: $(top_srcdir)/ccan/check_type/check_type.h ancdata.o: $(top_srcdir)/ccan/container_of/container_of.h ancdata.o: $(top_srcdir)/ccan/list/list.h ancdata.o: $(top_srcdir)/ccan/str/str.h +ancdata.o: $(top_srcdir)/encindex.h +ancdata.o: $(top_srcdir)/id_table.h ancdata.o: $(top_srcdir)/internal.h ancdata.o: $(top_srcdir)/internal/array.h ancdata.o: $(top_srcdir)/internal/basic_operators.h +ancdata.o: $(top_srcdir)/internal/box.h ancdata.o: $(top_srcdir)/internal/compilers.h ancdata.o: $(top_srcdir)/internal/error.h ancdata.o: $(top_srcdir)/internal/gc.h @@ -201,6 +206,7 @@ ancdata.o: $(top_srcdir)/internal/imemo.h ancdata.o: $(top_srcdir)/internal/io.h ancdata.o: $(top_srcdir)/internal/sanitizers.h ancdata.o: $(top_srcdir)/internal/serial.h +ancdata.o: $(top_srcdir)/internal/set_table.h ancdata.o: $(top_srcdir)/internal/static_assert.h ancdata.o: $(top_srcdir)/internal/string.h ancdata.o: $(top_srcdir)/internal/thread.h @@ -361,6 +367,7 @@ basicsocket.o: $(hdrdir)/ruby/internal/intern/re.h basicsocket.o: $(hdrdir)/ruby/internal/intern/ruby.h basicsocket.o: $(hdrdir)/ruby/internal/intern/select.h basicsocket.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/set.h basicsocket.o: $(hdrdir)/ruby/internal/intern/signal.h basicsocket.o: $(hdrdir)/ruby/internal/intern/sprintf.h basicsocket.o: $(hdrdir)/ruby/internal/intern/string.h @@ -380,6 +387,7 @@ basicsocket.o: $(hdrdir)/ruby/internal/special_consts.h basicsocket.o: $(hdrdir)/ruby/internal/static_assert.h basicsocket.o: $(hdrdir)/ruby/internal/stdalign.h basicsocket.o: $(hdrdir)/ruby/internal/stdbool.h +basicsocket.o: $(hdrdir)/ruby/internal/stdckdint.h basicsocket.o: $(hdrdir)/ruby/internal/symbol.h basicsocket.o: $(hdrdir)/ruby/internal/value.h basicsocket.o: $(hdrdir)/ruby/internal/value_type.h @@ -401,9 +409,12 @@ basicsocket.o: $(top_srcdir)/ccan/check_type/check_type.h basicsocket.o: $(top_srcdir)/ccan/container_of/container_of.h basicsocket.o: $(top_srcdir)/ccan/list/list.h basicsocket.o: $(top_srcdir)/ccan/str/str.h +basicsocket.o: $(top_srcdir)/encindex.h +basicsocket.o: $(top_srcdir)/id_table.h basicsocket.o: $(top_srcdir)/internal.h basicsocket.o: $(top_srcdir)/internal/array.h basicsocket.o: $(top_srcdir)/internal/basic_operators.h +basicsocket.o: $(top_srcdir)/internal/box.h basicsocket.o: $(top_srcdir)/internal/compilers.h basicsocket.o: $(top_srcdir)/internal/error.h basicsocket.o: $(top_srcdir)/internal/gc.h @@ -411,6 +422,7 @@ basicsocket.o: $(top_srcdir)/internal/imemo.h basicsocket.o: $(top_srcdir)/internal/io.h basicsocket.o: $(top_srcdir)/internal/sanitizers.h basicsocket.o: $(top_srcdir)/internal/serial.h +basicsocket.o: $(top_srcdir)/internal/set_table.h basicsocket.o: $(top_srcdir)/internal/static_assert.h basicsocket.o: $(top_srcdir)/internal/string.h basicsocket.o: $(top_srcdir)/internal/thread.h @@ -571,6 +583,7 @@ constants.o: $(hdrdir)/ruby/internal/intern/re.h constants.o: $(hdrdir)/ruby/internal/intern/ruby.h constants.o: $(hdrdir)/ruby/internal/intern/select.h constants.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +constants.o: $(hdrdir)/ruby/internal/intern/set.h constants.o: $(hdrdir)/ruby/internal/intern/signal.h constants.o: $(hdrdir)/ruby/internal/intern/sprintf.h constants.o: $(hdrdir)/ruby/internal/intern/string.h @@ -590,6 +603,7 @@ constants.o: $(hdrdir)/ruby/internal/special_consts.h constants.o: $(hdrdir)/ruby/internal/static_assert.h constants.o: $(hdrdir)/ruby/internal/stdalign.h constants.o: $(hdrdir)/ruby/internal/stdbool.h +constants.o: $(hdrdir)/ruby/internal/stdckdint.h constants.o: $(hdrdir)/ruby/internal/symbol.h constants.o: $(hdrdir)/ruby/internal/value.h constants.o: $(hdrdir)/ruby/internal/value_type.h @@ -611,9 +625,12 @@ constants.o: $(top_srcdir)/ccan/check_type/check_type.h constants.o: $(top_srcdir)/ccan/container_of/container_of.h constants.o: $(top_srcdir)/ccan/list/list.h constants.o: $(top_srcdir)/ccan/str/str.h +constants.o: $(top_srcdir)/encindex.h +constants.o: $(top_srcdir)/id_table.h constants.o: $(top_srcdir)/internal.h constants.o: $(top_srcdir)/internal/array.h constants.o: $(top_srcdir)/internal/basic_operators.h +constants.o: $(top_srcdir)/internal/box.h constants.o: $(top_srcdir)/internal/compilers.h constants.o: $(top_srcdir)/internal/error.h constants.o: $(top_srcdir)/internal/gc.h @@ -621,6 +638,7 @@ constants.o: $(top_srcdir)/internal/imemo.h constants.o: $(top_srcdir)/internal/io.h constants.o: $(top_srcdir)/internal/sanitizers.h constants.o: $(top_srcdir)/internal/serial.h +constants.o: $(top_srcdir)/internal/set_table.h constants.o: $(top_srcdir)/internal/static_assert.h constants.o: $(top_srcdir)/internal/string.h constants.o: $(top_srcdir)/internal/thread.h @@ -782,6 +800,7 @@ ifaddr.o: $(hdrdir)/ruby/internal/intern/re.h ifaddr.o: $(hdrdir)/ruby/internal/intern/ruby.h ifaddr.o: $(hdrdir)/ruby/internal/intern/select.h ifaddr.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/set.h ifaddr.o: $(hdrdir)/ruby/internal/intern/signal.h ifaddr.o: $(hdrdir)/ruby/internal/intern/sprintf.h ifaddr.o: $(hdrdir)/ruby/internal/intern/string.h @@ -801,6 +820,7 @@ ifaddr.o: $(hdrdir)/ruby/internal/special_consts.h ifaddr.o: $(hdrdir)/ruby/internal/static_assert.h ifaddr.o: $(hdrdir)/ruby/internal/stdalign.h ifaddr.o: $(hdrdir)/ruby/internal/stdbool.h +ifaddr.o: $(hdrdir)/ruby/internal/stdckdint.h ifaddr.o: $(hdrdir)/ruby/internal/symbol.h ifaddr.o: $(hdrdir)/ruby/internal/value.h ifaddr.o: $(hdrdir)/ruby/internal/value_type.h @@ -822,9 +842,12 @@ ifaddr.o: $(top_srcdir)/ccan/check_type/check_type.h ifaddr.o: $(top_srcdir)/ccan/container_of/container_of.h ifaddr.o: $(top_srcdir)/ccan/list/list.h ifaddr.o: $(top_srcdir)/ccan/str/str.h +ifaddr.o: $(top_srcdir)/encindex.h +ifaddr.o: $(top_srcdir)/id_table.h ifaddr.o: $(top_srcdir)/internal.h ifaddr.o: $(top_srcdir)/internal/array.h ifaddr.o: $(top_srcdir)/internal/basic_operators.h +ifaddr.o: $(top_srcdir)/internal/box.h ifaddr.o: $(top_srcdir)/internal/compilers.h ifaddr.o: $(top_srcdir)/internal/error.h ifaddr.o: $(top_srcdir)/internal/gc.h @@ -832,6 +855,7 @@ ifaddr.o: $(top_srcdir)/internal/imemo.h ifaddr.o: $(top_srcdir)/internal/io.h ifaddr.o: $(top_srcdir)/internal/sanitizers.h ifaddr.o: $(top_srcdir)/internal/serial.h +ifaddr.o: $(top_srcdir)/internal/set_table.h ifaddr.o: $(top_srcdir)/internal/static_assert.h ifaddr.o: $(top_srcdir)/internal/string.h ifaddr.o: $(top_srcdir)/internal/thread.h @@ -992,6 +1016,7 @@ init.o: $(hdrdir)/ruby/internal/intern/re.h init.o: $(hdrdir)/ruby/internal/intern/ruby.h init.o: $(hdrdir)/ruby/internal/intern/select.h init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h init.o: $(hdrdir)/ruby/internal/intern/signal.h init.o: $(hdrdir)/ruby/internal/intern/sprintf.h init.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1011,6 +1036,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -1032,9 +1058,12 @@ init.o: $(top_srcdir)/ccan/check_type/check_type.h init.o: $(top_srcdir)/ccan/container_of/container_of.h init.o: $(top_srcdir)/ccan/list/list.h init.o: $(top_srcdir)/ccan/str/str.h +init.o: $(top_srcdir)/encindex.h +init.o: $(top_srcdir)/id_table.h init.o: $(top_srcdir)/internal.h init.o: $(top_srcdir)/internal/array.h init.o: $(top_srcdir)/internal/basic_operators.h +init.o: $(top_srcdir)/internal/box.h init.o: $(top_srcdir)/internal/compilers.h init.o: $(top_srcdir)/internal/error.h init.o: $(top_srcdir)/internal/gc.h @@ -1042,6 +1071,7 @@ init.o: $(top_srcdir)/internal/imemo.h init.o: $(top_srcdir)/internal/io.h init.o: $(top_srcdir)/internal/sanitizers.h init.o: $(top_srcdir)/internal/serial.h +init.o: $(top_srcdir)/internal/set_table.h init.o: $(top_srcdir)/internal/static_assert.h init.o: $(top_srcdir)/internal/string.h init.o: $(top_srcdir)/internal/thread.h @@ -1202,6 +1232,7 @@ ipsocket.o: $(hdrdir)/ruby/internal/intern/re.h ipsocket.o: $(hdrdir)/ruby/internal/intern/ruby.h ipsocket.o: $(hdrdir)/ruby/internal/intern/select.h ipsocket.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/set.h ipsocket.o: $(hdrdir)/ruby/internal/intern/signal.h ipsocket.o: $(hdrdir)/ruby/internal/intern/sprintf.h ipsocket.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1221,6 +1252,7 @@ ipsocket.o: $(hdrdir)/ruby/internal/special_consts.h ipsocket.o: $(hdrdir)/ruby/internal/static_assert.h ipsocket.o: $(hdrdir)/ruby/internal/stdalign.h ipsocket.o: $(hdrdir)/ruby/internal/stdbool.h +ipsocket.o: $(hdrdir)/ruby/internal/stdckdint.h ipsocket.o: $(hdrdir)/ruby/internal/symbol.h ipsocket.o: $(hdrdir)/ruby/internal/value.h ipsocket.o: $(hdrdir)/ruby/internal/value_type.h @@ -1242,9 +1274,12 @@ ipsocket.o: $(top_srcdir)/ccan/check_type/check_type.h ipsocket.o: $(top_srcdir)/ccan/container_of/container_of.h ipsocket.o: $(top_srcdir)/ccan/list/list.h ipsocket.o: $(top_srcdir)/ccan/str/str.h +ipsocket.o: $(top_srcdir)/encindex.h +ipsocket.o: $(top_srcdir)/id_table.h ipsocket.o: $(top_srcdir)/internal.h ipsocket.o: $(top_srcdir)/internal/array.h ipsocket.o: $(top_srcdir)/internal/basic_operators.h +ipsocket.o: $(top_srcdir)/internal/box.h ipsocket.o: $(top_srcdir)/internal/compilers.h ipsocket.o: $(top_srcdir)/internal/error.h ipsocket.o: $(top_srcdir)/internal/gc.h @@ -1252,6 +1287,7 @@ ipsocket.o: $(top_srcdir)/internal/imemo.h ipsocket.o: $(top_srcdir)/internal/io.h ipsocket.o: $(top_srcdir)/internal/sanitizers.h ipsocket.o: $(top_srcdir)/internal/serial.h +ipsocket.o: $(top_srcdir)/internal/set_table.h ipsocket.o: $(top_srcdir)/internal/static_assert.h ipsocket.o: $(top_srcdir)/internal/string.h ipsocket.o: $(top_srcdir)/internal/thread.h @@ -1412,6 +1448,7 @@ option.o: $(hdrdir)/ruby/internal/intern/re.h option.o: $(hdrdir)/ruby/internal/intern/ruby.h option.o: $(hdrdir)/ruby/internal/intern/select.h option.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +option.o: $(hdrdir)/ruby/internal/intern/set.h option.o: $(hdrdir)/ruby/internal/intern/signal.h option.o: $(hdrdir)/ruby/internal/intern/sprintf.h option.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1431,6 +1468,7 @@ option.o: $(hdrdir)/ruby/internal/special_consts.h option.o: $(hdrdir)/ruby/internal/static_assert.h option.o: $(hdrdir)/ruby/internal/stdalign.h option.o: $(hdrdir)/ruby/internal/stdbool.h +option.o: $(hdrdir)/ruby/internal/stdckdint.h option.o: $(hdrdir)/ruby/internal/symbol.h option.o: $(hdrdir)/ruby/internal/value.h option.o: $(hdrdir)/ruby/internal/value_type.h @@ -1452,9 +1490,12 @@ option.o: $(top_srcdir)/ccan/check_type/check_type.h option.o: $(top_srcdir)/ccan/container_of/container_of.h option.o: $(top_srcdir)/ccan/list/list.h option.o: $(top_srcdir)/ccan/str/str.h +option.o: $(top_srcdir)/encindex.h +option.o: $(top_srcdir)/id_table.h option.o: $(top_srcdir)/internal.h option.o: $(top_srcdir)/internal/array.h option.o: $(top_srcdir)/internal/basic_operators.h +option.o: $(top_srcdir)/internal/box.h option.o: $(top_srcdir)/internal/compilers.h option.o: $(top_srcdir)/internal/error.h option.o: $(top_srcdir)/internal/gc.h @@ -1462,6 +1503,7 @@ option.o: $(top_srcdir)/internal/imemo.h option.o: $(top_srcdir)/internal/io.h option.o: $(top_srcdir)/internal/sanitizers.h option.o: $(top_srcdir)/internal/serial.h +option.o: $(top_srcdir)/internal/set_table.h option.o: $(top_srcdir)/internal/static_assert.h option.o: $(top_srcdir)/internal/string.h option.o: $(top_srcdir)/internal/thread.h @@ -1622,6 +1664,7 @@ raddrinfo.o: $(hdrdir)/ruby/internal/intern/re.h raddrinfo.o: $(hdrdir)/ruby/internal/intern/ruby.h raddrinfo.o: $(hdrdir)/ruby/internal/intern/select.h raddrinfo.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/set.h raddrinfo.o: $(hdrdir)/ruby/internal/intern/signal.h raddrinfo.o: $(hdrdir)/ruby/internal/intern/sprintf.h raddrinfo.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1641,6 +1684,7 @@ raddrinfo.o: $(hdrdir)/ruby/internal/special_consts.h raddrinfo.o: $(hdrdir)/ruby/internal/static_assert.h raddrinfo.o: $(hdrdir)/ruby/internal/stdalign.h raddrinfo.o: $(hdrdir)/ruby/internal/stdbool.h +raddrinfo.o: $(hdrdir)/ruby/internal/stdckdint.h raddrinfo.o: $(hdrdir)/ruby/internal/symbol.h raddrinfo.o: $(hdrdir)/ruby/internal/value.h raddrinfo.o: $(hdrdir)/ruby/internal/value_type.h @@ -1662,9 +1706,12 @@ raddrinfo.o: $(top_srcdir)/ccan/check_type/check_type.h raddrinfo.o: $(top_srcdir)/ccan/container_of/container_of.h raddrinfo.o: $(top_srcdir)/ccan/list/list.h raddrinfo.o: $(top_srcdir)/ccan/str/str.h +raddrinfo.o: $(top_srcdir)/encindex.h +raddrinfo.o: $(top_srcdir)/id_table.h raddrinfo.o: $(top_srcdir)/internal.h raddrinfo.o: $(top_srcdir)/internal/array.h raddrinfo.o: $(top_srcdir)/internal/basic_operators.h +raddrinfo.o: $(top_srcdir)/internal/box.h raddrinfo.o: $(top_srcdir)/internal/compilers.h raddrinfo.o: $(top_srcdir)/internal/error.h raddrinfo.o: $(top_srcdir)/internal/gc.h @@ -1672,6 +1719,7 @@ raddrinfo.o: $(top_srcdir)/internal/imemo.h raddrinfo.o: $(top_srcdir)/internal/io.h raddrinfo.o: $(top_srcdir)/internal/sanitizers.h raddrinfo.o: $(top_srcdir)/internal/serial.h +raddrinfo.o: $(top_srcdir)/internal/set_table.h raddrinfo.o: $(top_srcdir)/internal/static_assert.h raddrinfo.o: $(top_srcdir)/internal/string.h raddrinfo.o: $(top_srcdir)/internal/thread.h @@ -1832,6 +1880,7 @@ socket.o: $(hdrdir)/ruby/internal/intern/re.h socket.o: $(hdrdir)/ruby/internal/intern/ruby.h socket.o: $(hdrdir)/ruby/internal/intern/select.h socket.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +socket.o: $(hdrdir)/ruby/internal/intern/set.h socket.o: $(hdrdir)/ruby/internal/intern/signal.h socket.o: $(hdrdir)/ruby/internal/intern/sprintf.h socket.o: $(hdrdir)/ruby/internal/intern/string.h @@ -1851,6 +1900,7 @@ socket.o: $(hdrdir)/ruby/internal/special_consts.h socket.o: $(hdrdir)/ruby/internal/static_assert.h socket.o: $(hdrdir)/ruby/internal/stdalign.h socket.o: $(hdrdir)/ruby/internal/stdbool.h +socket.o: $(hdrdir)/ruby/internal/stdckdint.h socket.o: $(hdrdir)/ruby/internal/symbol.h socket.o: $(hdrdir)/ruby/internal/value.h socket.o: $(hdrdir)/ruby/internal/value_type.h @@ -1872,9 +1922,12 @@ socket.o: $(top_srcdir)/ccan/check_type/check_type.h socket.o: $(top_srcdir)/ccan/container_of/container_of.h socket.o: $(top_srcdir)/ccan/list/list.h socket.o: $(top_srcdir)/ccan/str/str.h +socket.o: $(top_srcdir)/encindex.h +socket.o: $(top_srcdir)/id_table.h socket.o: $(top_srcdir)/internal.h socket.o: $(top_srcdir)/internal/array.h socket.o: $(top_srcdir)/internal/basic_operators.h +socket.o: $(top_srcdir)/internal/box.h socket.o: $(top_srcdir)/internal/compilers.h socket.o: $(top_srcdir)/internal/error.h socket.o: $(top_srcdir)/internal/gc.h @@ -1882,6 +1935,7 @@ socket.o: $(top_srcdir)/internal/imemo.h socket.o: $(top_srcdir)/internal/io.h socket.o: $(top_srcdir)/internal/sanitizers.h socket.o: $(top_srcdir)/internal/serial.h +socket.o: $(top_srcdir)/internal/set_table.h socket.o: $(top_srcdir)/internal/static_assert.h socket.o: $(top_srcdir)/internal/string.h socket.o: $(top_srcdir)/internal/thread.h @@ -2042,6 +2096,7 @@ sockssocket.o: $(hdrdir)/ruby/internal/intern/re.h sockssocket.o: $(hdrdir)/ruby/internal/intern/ruby.h sockssocket.o: $(hdrdir)/ruby/internal/intern/select.h sockssocket.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/set.h sockssocket.o: $(hdrdir)/ruby/internal/intern/signal.h sockssocket.o: $(hdrdir)/ruby/internal/intern/sprintf.h sockssocket.o: $(hdrdir)/ruby/internal/intern/string.h @@ -2061,6 +2116,7 @@ sockssocket.o: $(hdrdir)/ruby/internal/special_consts.h sockssocket.o: $(hdrdir)/ruby/internal/static_assert.h sockssocket.o: $(hdrdir)/ruby/internal/stdalign.h sockssocket.o: $(hdrdir)/ruby/internal/stdbool.h +sockssocket.o: $(hdrdir)/ruby/internal/stdckdint.h sockssocket.o: $(hdrdir)/ruby/internal/symbol.h sockssocket.o: $(hdrdir)/ruby/internal/value.h sockssocket.o: $(hdrdir)/ruby/internal/value_type.h @@ -2082,9 +2138,12 @@ sockssocket.o: $(top_srcdir)/ccan/check_type/check_type.h sockssocket.o: $(top_srcdir)/ccan/container_of/container_of.h sockssocket.o: $(top_srcdir)/ccan/list/list.h sockssocket.o: $(top_srcdir)/ccan/str/str.h +sockssocket.o: $(top_srcdir)/encindex.h +sockssocket.o: $(top_srcdir)/id_table.h sockssocket.o: $(top_srcdir)/internal.h sockssocket.o: $(top_srcdir)/internal/array.h sockssocket.o: $(top_srcdir)/internal/basic_operators.h +sockssocket.o: $(top_srcdir)/internal/box.h sockssocket.o: $(top_srcdir)/internal/compilers.h sockssocket.o: $(top_srcdir)/internal/error.h sockssocket.o: $(top_srcdir)/internal/gc.h @@ -2092,6 +2151,7 @@ sockssocket.o: $(top_srcdir)/internal/imemo.h sockssocket.o: $(top_srcdir)/internal/io.h sockssocket.o: $(top_srcdir)/internal/sanitizers.h sockssocket.o: $(top_srcdir)/internal/serial.h +sockssocket.o: $(top_srcdir)/internal/set_table.h sockssocket.o: $(top_srcdir)/internal/static_assert.h sockssocket.o: $(top_srcdir)/internal/string.h sockssocket.o: $(top_srcdir)/internal/thread.h @@ -2252,6 +2312,7 @@ tcpserver.o: $(hdrdir)/ruby/internal/intern/re.h tcpserver.o: $(hdrdir)/ruby/internal/intern/ruby.h tcpserver.o: $(hdrdir)/ruby/internal/intern/select.h tcpserver.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/set.h tcpserver.o: $(hdrdir)/ruby/internal/intern/signal.h tcpserver.o: $(hdrdir)/ruby/internal/intern/sprintf.h tcpserver.o: $(hdrdir)/ruby/internal/intern/string.h @@ -2271,6 +2332,7 @@ tcpserver.o: $(hdrdir)/ruby/internal/special_consts.h tcpserver.o: $(hdrdir)/ruby/internal/static_assert.h tcpserver.o: $(hdrdir)/ruby/internal/stdalign.h tcpserver.o: $(hdrdir)/ruby/internal/stdbool.h +tcpserver.o: $(hdrdir)/ruby/internal/stdckdint.h tcpserver.o: $(hdrdir)/ruby/internal/symbol.h tcpserver.o: $(hdrdir)/ruby/internal/value.h tcpserver.o: $(hdrdir)/ruby/internal/value_type.h @@ -2292,9 +2354,12 @@ tcpserver.o: $(top_srcdir)/ccan/check_type/check_type.h tcpserver.o: $(top_srcdir)/ccan/container_of/container_of.h tcpserver.o: $(top_srcdir)/ccan/list/list.h tcpserver.o: $(top_srcdir)/ccan/str/str.h +tcpserver.o: $(top_srcdir)/encindex.h +tcpserver.o: $(top_srcdir)/id_table.h tcpserver.o: $(top_srcdir)/internal.h tcpserver.o: $(top_srcdir)/internal/array.h tcpserver.o: $(top_srcdir)/internal/basic_operators.h +tcpserver.o: $(top_srcdir)/internal/box.h tcpserver.o: $(top_srcdir)/internal/compilers.h tcpserver.o: $(top_srcdir)/internal/error.h tcpserver.o: $(top_srcdir)/internal/gc.h @@ -2302,6 +2367,7 @@ tcpserver.o: $(top_srcdir)/internal/imemo.h tcpserver.o: $(top_srcdir)/internal/io.h tcpserver.o: $(top_srcdir)/internal/sanitizers.h tcpserver.o: $(top_srcdir)/internal/serial.h +tcpserver.o: $(top_srcdir)/internal/set_table.h tcpserver.o: $(top_srcdir)/internal/static_assert.h tcpserver.o: $(top_srcdir)/internal/string.h tcpserver.o: $(top_srcdir)/internal/thread.h @@ -2462,6 +2528,7 @@ tcpsocket.o: $(hdrdir)/ruby/internal/intern/re.h tcpsocket.o: $(hdrdir)/ruby/internal/intern/ruby.h tcpsocket.o: $(hdrdir)/ruby/internal/intern/select.h tcpsocket.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/set.h tcpsocket.o: $(hdrdir)/ruby/internal/intern/signal.h tcpsocket.o: $(hdrdir)/ruby/internal/intern/sprintf.h tcpsocket.o: $(hdrdir)/ruby/internal/intern/string.h @@ -2481,6 +2548,7 @@ tcpsocket.o: $(hdrdir)/ruby/internal/special_consts.h tcpsocket.o: $(hdrdir)/ruby/internal/static_assert.h tcpsocket.o: $(hdrdir)/ruby/internal/stdalign.h tcpsocket.o: $(hdrdir)/ruby/internal/stdbool.h +tcpsocket.o: $(hdrdir)/ruby/internal/stdckdint.h tcpsocket.o: $(hdrdir)/ruby/internal/symbol.h tcpsocket.o: $(hdrdir)/ruby/internal/value.h tcpsocket.o: $(hdrdir)/ruby/internal/value_type.h @@ -2502,9 +2570,12 @@ tcpsocket.o: $(top_srcdir)/ccan/check_type/check_type.h tcpsocket.o: $(top_srcdir)/ccan/container_of/container_of.h tcpsocket.o: $(top_srcdir)/ccan/list/list.h tcpsocket.o: $(top_srcdir)/ccan/str/str.h +tcpsocket.o: $(top_srcdir)/encindex.h +tcpsocket.o: $(top_srcdir)/id_table.h tcpsocket.o: $(top_srcdir)/internal.h tcpsocket.o: $(top_srcdir)/internal/array.h tcpsocket.o: $(top_srcdir)/internal/basic_operators.h +tcpsocket.o: $(top_srcdir)/internal/box.h tcpsocket.o: $(top_srcdir)/internal/compilers.h tcpsocket.o: $(top_srcdir)/internal/error.h tcpsocket.o: $(top_srcdir)/internal/gc.h @@ -2512,6 +2583,7 @@ tcpsocket.o: $(top_srcdir)/internal/imemo.h tcpsocket.o: $(top_srcdir)/internal/io.h tcpsocket.o: $(top_srcdir)/internal/sanitizers.h tcpsocket.o: $(top_srcdir)/internal/serial.h +tcpsocket.o: $(top_srcdir)/internal/set_table.h tcpsocket.o: $(top_srcdir)/internal/static_assert.h tcpsocket.o: $(top_srcdir)/internal/string.h tcpsocket.o: $(top_srcdir)/internal/thread.h @@ -2672,6 +2744,7 @@ udpsocket.o: $(hdrdir)/ruby/internal/intern/re.h udpsocket.o: $(hdrdir)/ruby/internal/intern/ruby.h udpsocket.o: $(hdrdir)/ruby/internal/intern/select.h udpsocket.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/set.h udpsocket.o: $(hdrdir)/ruby/internal/intern/signal.h udpsocket.o: $(hdrdir)/ruby/internal/intern/sprintf.h udpsocket.o: $(hdrdir)/ruby/internal/intern/string.h @@ -2691,6 +2764,7 @@ udpsocket.o: $(hdrdir)/ruby/internal/special_consts.h udpsocket.o: $(hdrdir)/ruby/internal/static_assert.h udpsocket.o: $(hdrdir)/ruby/internal/stdalign.h udpsocket.o: $(hdrdir)/ruby/internal/stdbool.h +udpsocket.o: $(hdrdir)/ruby/internal/stdckdint.h udpsocket.o: $(hdrdir)/ruby/internal/symbol.h udpsocket.o: $(hdrdir)/ruby/internal/value.h udpsocket.o: $(hdrdir)/ruby/internal/value_type.h @@ -2712,9 +2786,12 @@ udpsocket.o: $(top_srcdir)/ccan/check_type/check_type.h udpsocket.o: $(top_srcdir)/ccan/container_of/container_of.h udpsocket.o: $(top_srcdir)/ccan/list/list.h udpsocket.o: $(top_srcdir)/ccan/str/str.h +udpsocket.o: $(top_srcdir)/encindex.h +udpsocket.o: $(top_srcdir)/id_table.h udpsocket.o: $(top_srcdir)/internal.h udpsocket.o: $(top_srcdir)/internal/array.h udpsocket.o: $(top_srcdir)/internal/basic_operators.h +udpsocket.o: $(top_srcdir)/internal/box.h udpsocket.o: $(top_srcdir)/internal/compilers.h udpsocket.o: $(top_srcdir)/internal/error.h udpsocket.o: $(top_srcdir)/internal/gc.h @@ -2722,6 +2799,7 @@ udpsocket.o: $(top_srcdir)/internal/imemo.h udpsocket.o: $(top_srcdir)/internal/io.h udpsocket.o: $(top_srcdir)/internal/sanitizers.h udpsocket.o: $(top_srcdir)/internal/serial.h +udpsocket.o: $(top_srcdir)/internal/set_table.h udpsocket.o: $(top_srcdir)/internal/static_assert.h udpsocket.o: $(top_srcdir)/internal/string.h udpsocket.o: $(top_srcdir)/internal/thread.h @@ -2882,6 +2960,7 @@ unixserver.o: $(hdrdir)/ruby/internal/intern/re.h unixserver.o: $(hdrdir)/ruby/internal/intern/ruby.h unixserver.o: $(hdrdir)/ruby/internal/intern/select.h unixserver.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +unixserver.o: $(hdrdir)/ruby/internal/intern/set.h unixserver.o: $(hdrdir)/ruby/internal/intern/signal.h unixserver.o: $(hdrdir)/ruby/internal/intern/sprintf.h unixserver.o: $(hdrdir)/ruby/internal/intern/string.h @@ -2901,6 +2980,7 @@ unixserver.o: $(hdrdir)/ruby/internal/special_consts.h unixserver.o: $(hdrdir)/ruby/internal/static_assert.h unixserver.o: $(hdrdir)/ruby/internal/stdalign.h unixserver.o: $(hdrdir)/ruby/internal/stdbool.h +unixserver.o: $(hdrdir)/ruby/internal/stdckdint.h unixserver.o: $(hdrdir)/ruby/internal/symbol.h unixserver.o: $(hdrdir)/ruby/internal/value.h unixserver.o: $(hdrdir)/ruby/internal/value_type.h @@ -2922,9 +3002,12 @@ unixserver.o: $(top_srcdir)/ccan/check_type/check_type.h unixserver.o: $(top_srcdir)/ccan/container_of/container_of.h unixserver.o: $(top_srcdir)/ccan/list/list.h unixserver.o: $(top_srcdir)/ccan/str/str.h +unixserver.o: $(top_srcdir)/encindex.h +unixserver.o: $(top_srcdir)/id_table.h unixserver.o: $(top_srcdir)/internal.h unixserver.o: $(top_srcdir)/internal/array.h unixserver.o: $(top_srcdir)/internal/basic_operators.h +unixserver.o: $(top_srcdir)/internal/box.h unixserver.o: $(top_srcdir)/internal/compilers.h unixserver.o: $(top_srcdir)/internal/error.h unixserver.o: $(top_srcdir)/internal/gc.h @@ -2932,6 +3015,7 @@ unixserver.o: $(top_srcdir)/internal/imemo.h unixserver.o: $(top_srcdir)/internal/io.h unixserver.o: $(top_srcdir)/internal/sanitizers.h unixserver.o: $(top_srcdir)/internal/serial.h +unixserver.o: $(top_srcdir)/internal/set_table.h unixserver.o: $(top_srcdir)/internal/static_assert.h unixserver.o: $(top_srcdir)/internal/string.h unixserver.o: $(top_srcdir)/internal/thread.h @@ -3092,6 +3176,7 @@ unixsocket.o: $(hdrdir)/ruby/internal/intern/re.h unixsocket.o: $(hdrdir)/ruby/internal/intern/ruby.h unixsocket.o: $(hdrdir)/ruby/internal/intern/select.h unixsocket.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/set.h unixsocket.o: $(hdrdir)/ruby/internal/intern/signal.h unixsocket.o: $(hdrdir)/ruby/internal/intern/sprintf.h unixsocket.o: $(hdrdir)/ruby/internal/intern/string.h @@ -3111,6 +3196,7 @@ unixsocket.o: $(hdrdir)/ruby/internal/special_consts.h unixsocket.o: $(hdrdir)/ruby/internal/static_assert.h unixsocket.o: $(hdrdir)/ruby/internal/stdalign.h unixsocket.o: $(hdrdir)/ruby/internal/stdbool.h +unixsocket.o: $(hdrdir)/ruby/internal/stdckdint.h unixsocket.o: $(hdrdir)/ruby/internal/symbol.h unixsocket.o: $(hdrdir)/ruby/internal/value.h unixsocket.o: $(hdrdir)/ruby/internal/value_type.h @@ -3132,9 +3218,12 @@ unixsocket.o: $(top_srcdir)/ccan/check_type/check_type.h unixsocket.o: $(top_srcdir)/ccan/container_of/container_of.h unixsocket.o: $(top_srcdir)/ccan/list/list.h unixsocket.o: $(top_srcdir)/ccan/str/str.h +unixsocket.o: $(top_srcdir)/encindex.h +unixsocket.o: $(top_srcdir)/id_table.h unixsocket.o: $(top_srcdir)/internal.h unixsocket.o: $(top_srcdir)/internal/array.h unixsocket.o: $(top_srcdir)/internal/basic_operators.h +unixsocket.o: $(top_srcdir)/internal/box.h unixsocket.o: $(top_srcdir)/internal/compilers.h unixsocket.o: $(top_srcdir)/internal/error.h unixsocket.o: $(top_srcdir)/internal/gc.h @@ -3142,6 +3231,7 @@ unixsocket.o: $(top_srcdir)/internal/imemo.h unixsocket.o: $(top_srcdir)/internal/io.h unixsocket.o: $(top_srcdir)/internal/sanitizers.h unixsocket.o: $(top_srcdir)/internal/serial.h +unixsocket.o: $(top_srcdir)/internal/set_table.h unixsocket.o: $(top_srcdir)/internal/static_assert.h unixsocket.o: $(top_srcdir)/internal/string.h unixsocket.o: $(top_srcdir)/internal/thread.h diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb index 4e8536fc60..a814e21c3a 100644 --- a/ext/socket/extconf.rb +++ b/ext/socket/extconf.rb @@ -607,8 +607,6 @@ You can try --enable-wide-getaddrinfo. EOS end - have_const('AI_ADDRCONFIG', headers) - case with_config("lookup-order-hack", "UNSPEC") when "INET" $defs << "-DLOOKUP_ORDER_HACK_INET" @@ -706,6 +704,7 @@ SRC have_func("pthread_create") have_func("pthread_detach") + have_func("pthread_attr_setdetachstate") $VPATH << '$(topdir)' << '$(top_srcdir)' create_makefile("socket") diff --git a/ext/socket/getaddrinfo.c b/ext/socket/getaddrinfo.c index bf0d90129f..9a65490b1d 100644 --- a/ext/socket/getaddrinfo.c +++ b/ext/socket/getaddrinfo.c @@ -62,9 +62,6 @@ #endif #include <unistd.h> #else -#if defined(_MSC_VER) && _MSC_VER <= 1200 -#include <windows.h> -#endif #include <winsock2.h> #include <ws2tcpip.h> #include <io.h> @@ -171,9 +168,7 @@ static const char *const ai_errlist[] = { #define GET_CANONNAME(ai, str) \ if (pai->ai_flags & AI_CANONNAME) {\ - if (((ai)->ai_canonname = (char *)malloc(strlen(str) + 1)) != NULL) {\ - strcpy((ai)->ai_canonname, (str));\ - } else {\ + if (((ai)->ai_canonname = strdup(str)) == NULL) {\ error = EAI_MEMORY;\ goto free;\ }\ diff --git a/ext/socket/getnameinfo.c b/ext/socket/getnameinfo.c index ae5284fab6..98da8c1647 100644 --- a/ext/socket/getnameinfo.c +++ b/ext/socket/getnameinfo.c @@ -55,9 +55,6 @@ #endif #endif #ifdef _WIN32 -#if defined(_MSC_VER) && _MSC_VER <= 1200 -#include <windows.h> -#endif #include <winsock2.h> #include <ws2tcpip.h> #define snprintf _snprintf @@ -158,16 +155,14 @@ getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, socklen_t ho /* what we should do? */ } else if (flags & NI_NUMERICSERV) { snprintf(numserv, sizeof(numserv), "%d", ntohs(port)); - if (strlen(numserv) + 1 > servlen) + if (strlcpy(serv, numserv, servlen) >= servlen) return ENI_MEMORY; - strcpy(serv, numserv); } else { #if defined(HAVE_GETSERVBYPORT) struct servent *sp = getservbyport(port, (flags & NI_DGRAM) ? "udp" : "tcp"); if (sp) { - if (strlen(sp->s_name) + 1 > servlen) + if (strlcpy(serv, sp->s_name, servlen) >= servlen) return ENI_MEMORY; - strcpy(serv, sp->s_name); } else return ENI_NOSERVNAME; #else @@ -202,9 +197,8 @@ getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, socklen_t ho if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) == NULL) return ENI_SYSTEM; - if (strlen(numaddr) > hostlen) + if (strlcpy(host, numaddr, hostlen) >= hostlen) return ENI_MEMORY; - strcpy(host, numaddr); } else { #ifdef INET6 hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); @@ -218,13 +212,12 @@ getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, socklen_t ho p = strchr(hp->h_name, '.'); if (p) *p = '\0'; } - if (strlen(hp->h_name) + 1 > hostlen) { + if (strlcpy(host, hp->h_name, hostlen) >= hostlen) { #ifdef INET6 freehostent(hp); #endif return ENI_MEMORY; } - strcpy(host, hp->h_name); #ifdef INET6 freehostent(hp); #endif @@ -234,9 +227,8 @@ getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, socklen_t ho if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) == NULL) return ENI_NOHOSTNAME; - if (strlen(numaddr) > hostlen) + if (strlcpy(host, numaddr, hostlen) >= hostlen) return ENI_MEMORY; - strcpy(host, numaddr); } } return SUCCESS; diff --git a/ext/socket/ifaddr.c b/ext/socket/ifaddr.c index ab163dcc8f..3596c40a11 100644 --- a/ext/socket/ifaddr.c +++ b/ext/socket/ifaddr.c @@ -177,6 +177,8 @@ ifaddr_ifindex(VALUE self) * ifaddr.flags => integer * * Returns the flags of _ifaddr_. + * + * The value is bitwise-or of Socket::IFF_* constants such as Socket::IFF_LOOPBACK. */ static VALUE diff --git a/ext/socket/init.c b/ext/socket/init.c index dd1158e19d..b761d601c3 100644 --- a/ext/socket/init.c +++ b/ext/socket/init.c @@ -107,6 +107,7 @@ rsock_send_blocking(void *data) } struct recvfrom_arg { + rb_io_t *fptr; int fd, flags; VALUE str; size_t length; @@ -151,7 +152,7 @@ recvfrom_locktmp(VALUE v) { struct recvfrom_arg *arg = (struct recvfrom_arg *)v; - return rb_thread_io_blocking_region(recvfrom_blocking, arg, arg->fd); + return rb_io_blocking_region(arg->fptr, recvfrom_blocking, arg); } int @@ -192,6 +193,7 @@ rsock_s_recvfrom(VALUE socket, int argc, VALUE *argv, enum sock_recv_type from) rb_raise(rb_eIOError, "recv for buffered IO"); } + arg.fptr = fptr; arg.fd = fptr->fd; arg.alen = (socklen_t)sizeof(arg.buf); arg.str = str; @@ -204,7 +206,8 @@ rsock_s_recvfrom(VALUE socket, int argc, VALUE *argv, enum sock_recv_type from) rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), Qnil); #endif - slen = (long)rb_str_locktmp_ensure(str, recvfrom_locktmp, (VALUE)&arg); + rb_str_locktmp(str); + slen = (long)rb_ensure(recvfrom_locktmp, (VALUE)&arg, rb_str_unlocktmp, str); if (slen == 0 && !rsock_is_dgram(fptr)) { return Qnil; @@ -470,10 +473,11 @@ rsock_socket(int domain, int type, int proto) /* emulate blocking connect behavior on EINTR or non-blocking socket */ static int -wait_connectable(int fd, struct timeval *timeout) +wait_connectable(VALUE self, VALUE timeout, const struct sockaddr *sockaddr, int len) { - int sockerr, revents; + int sockerr; socklen_t sockerrlen; + int fd = rb_io_descriptor(self); sockerrlen = (socklen_t)sizeof(sockerr); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen) < 0) @@ -507,7 +511,16 @@ wait_connectable(int fd, struct timeval *timeout) * * Note: rb_wait_for_single_fd already retries on EINTR/ERESTART */ - revents = rb_wait_for_single_fd(fd, RB_WAITFD_IN|RB_WAITFD_OUT, timeout); + VALUE result = rb_io_wait(self, RB_INT2NUM(RUBY_IO_READABLE|RUBY_IO_WRITABLE), timeout); + + if (result == Qfalse) { + VALUE rai = rsock_addrinfo_new((struct sockaddr *)sockaddr, len, PF_UNSPEC, 0, 0, Qnil, Qnil); + VALUE addr_str = rsock_addrinfo_inspect_sockaddr(rai); + VALUE message = rb_sprintf("user specified timeout for %" PRIsVALUE, addr_str); + rb_raise(rb_eIOTimeoutError, "%" PRIsVALUE, message); + } + + int revents = RB_NUM2INT(result); if (revents < 0) return -1; @@ -522,12 +535,6 @@ wait_connectable(int fd, struct timeval *timeout) * be defensive in case some platforms set SO_ERROR on the original, * interrupted connect() */ - - /* when the connection timed out, no errno is set and revents is 0. */ - if (timeout && revents == 0) { - errno = ETIMEDOUT; - return -1; - } case EINTR: #ifdef ERESTART case ERESTART: @@ -575,19 +582,19 @@ socks_connect_blocking(void *data) #endif int -rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks, struct timeval *timeout) +rsock_connect(VALUE self, const struct sockaddr *sockaddr, int len, int socks, VALUE timeout) { - int status; + int descriptor = rb_io_descriptor(self); rb_blocking_function_t *func = connect_blocking; - struct connect_arg arg; + struct connect_arg arg = {.fd = descriptor, .sockaddr = sockaddr, .len = len}; + + rb_io_t *fptr; + RB_IO_POINTER(self, fptr); - arg.fd = fd; - arg.sockaddr = sockaddr; - arg.len = len; #if defined(SOCKS) && !defined(SOCKS5) if (socks) func = socks_connect_blocking; #endif - status = (int)BLOCKING_REGION_FD(func, &arg); + int status = (int)rb_io_blocking_region(fptr, func, &arg); if (status < 0) { switch (errno) { @@ -599,7 +606,7 @@ rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks, struc #ifdef EINPROGRESS case EINPROGRESS: #endif - return wait_connectable(fd, timeout); + return wait_connectable(self, timeout, sockaddr, len); } } return status; @@ -718,7 +725,7 @@ rsock_s_accept(VALUE klass, VALUE io, struct sockaddr *sockaddr, socklen_t *len) #ifdef RSOCK_WAIT_BEFORE_BLOCKING rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), Qnil); #endif - peer = (int)BLOCKING_REGION_FD(accept_blocking, &accept_arg); + peer = (int)rb_io_blocking_region(fptr, accept_blocking, &accept_arg); if (peer < 0) { int error = errno; @@ -782,7 +789,17 @@ rsock_getfamily(rb_io_t *fptr) * call-seq: * error_code -> integer * - * Returns the raw error code occurred at name resolution. + * Returns the raw error code indicating the cause of the hostname resolution failure. + * + * begin + * Addrinfo.getaddrinfo("ruby-lang.org", nil) + * rescue Socket::ResolutionError => e + * if e.error_code == Socket::EAI_AGAIN + * puts "Temporary failure in name resolution." + * end + * end + * + * Note that error codes depend on the operating system. */ static VALUE sock_resolv_error_code(VALUE self) @@ -798,7 +815,7 @@ rsock_init_socket_init(void) */ rb_eSocket = rb_define_class("SocketError", rb_eStandardError); /* - * ResolutionError is the error class for socket name resolution. + * Socket::ResolutionError is the error class for hostname resolution. */ rb_eResolution = rb_define_class_under(rb_cSocket, "ResolutionError", rb_eSocket); rb_define_method(rb_eResolution, "error_code", sock_resolv_error_code, 0); diff --git a/ext/socket/ipsocket.c b/ext/socket/ipsocket.c index 0a693655b4..931a1a629c 100644 --- a/ext/socket/ipsocket.c +++ b/ext/socket/ipsocket.c @@ -9,20 +9,39 @@ ************************************************/ #include "rubysocket.h" +#include <stdio.h> struct inetsock_arg { - VALUE sock; + VALUE self; + VALUE io; + struct { VALUE host, serv; struct rb_addrinfo *res; } remote, local; int type; - int fd; VALUE resolv_timeout; VALUE connect_timeout; + VALUE open_timeout; }; +void +rsock_raise_user_specified_timeout(struct addrinfo *ai, VALUE host, VALUE port) +{ + VALUE message; + + if (ai && ai->ai_addr) { + VALUE rai = rsock_addrinfo_new((struct sockaddr *)ai->ai_addr, (socklen_t)ai->ai_addrlen, PF_UNSPEC, 0, 0, Qnil, Qnil); + VALUE addr_str = rsock_addrinfo_inspect_sockaddr(rai); + message = rb_sprintf("user specified timeout for %" PRIsVALUE, addr_str); + } else { + message = rb_sprintf("user specified timeout for %" PRIsVALUE " port %" PRIsVALUE, host, port); + } + + rb_raise(rb_eIOTimeoutError, "%" PRIsVALUE, message); +} + static VALUE inetsock_cleanup(VALUE v) { @@ -35,42 +54,42 @@ inetsock_cleanup(VALUE v) rb_freeaddrinfo(arg->local.res); arg->local.res = 0; } - if (arg->fd >= 0) { - close(arg->fd); + if (arg->io != Qnil) { + rb_io_close(arg->io); + arg->io = Qnil; } return Qnil; } static VALUE +current_clocktime(void) +{ + VALUE clock_monotnic_const = rb_const_get(rb_mProcess, rb_intern("CLOCK_MONOTONIC")); + return rb_funcall(rb_mProcess, rb_intern("clock_gettime"), 1, clock_monotnic_const); +} + +static VALUE init_inetsock_internal(VALUE v) { struct inetsock_arg *arg = (void *)v; int error = 0; int type = arg->type; struct addrinfo *res, *lres; - int fd, status = 0, local = 0; + int status = 0, local = 0; int family = AF_UNSPEC; const char *syscall = 0; + VALUE resolv_timeout = arg->resolv_timeout; VALUE connect_timeout = arg->connect_timeout; - struct timeval tv_storage; - struct timeval *tv = NULL; - int remote_addrinfo_hints = 0; - - if (!NIL_P(connect_timeout)) { - tv_storage = rb_time_interval(connect_timeout); - tv = &tv_storage; - } + VALUE open_timeout = arg->open_timeout; + VALUE timeout; + VALUE starts_at; - if (type == INET_SERVER) { - remote_addrinfo_hints |= AI_PASSIVE; - } -#ifdef HAVE_CONST_AI_ADDRCONFIG - remote_addrinfo_hints |= AI_ADDRCONFIG; -#endif + timeout = NIL_P(open_timeout) ? resolv_timeout : open_timeout; + starts_at = current_clocktime(); arg->remote.res = rsock_addrinfo(arg->remote.host, arg->remote.serv, - family, SOCK_STREAM, remote_addrinfo_hints); - + family, SOCK_STREAM, + (type == INET_SERVER) ? AI_PASSIVE : 0, timeout); /* * Maybe also accept a local address @@ -78,10 +97,11 @@ init_inetsock_internal(VALUE v) if (type != INET_SERVER && (!NIL_P(arg->local.host) || !NIL_P(arg->local.serv))) { arg->local.res = rsock_addrinfo(arg->local.host, arg->local.serv, - family, SOCK_STREAM, 0); + family, SOCK_STREAM, 0, timeout); } - arg->fd = fd = -1; + VALUE io = Qnil; + for (res = arg->remote.res->ai; res; res = res->ai_next) { #if !defined(INET6) && defined(AF_INET6) if (res->ai_family == AF_INET6) @@ -103,12 +123,14 @@ init_inetsock_internal(VALUE v) } status = rsock_socket(res->ai_family,res->ai_socktype,res->ai_protocol); syscall = "socket(2)"; - fd = status; - if (fd < 0) { + if (status < 0) { error = errno; continue; } - arg->fd = fd; + + int fd = status; + io = arg->io = rsock_init_sock(arg->self, fd); + if (type == INET_SERVER) { #if !defined(_WIN32) && !defined(__CYGWIN__) status = 1; @@ -130,21 +152,33 @@ init_inetsock_internal(VALUE v) syscall = "bind(2)"; } + if (NIL_P(open_timeout)) { + timeout = connect_timeout; + } else { + VALUE elapsed = rb_funcall(current_clocktime(), '-', 1, starts_at); + timeout = rb_funcall(open_timeout, '-', 1, elapsed); + if (rb_funcall(timeout, '<', 1, INT2FIX(0)) == Qtrue) { + rsock_raise_user_specified_timeout(res, arg->remote.host, arg->remote.serv); + } + } + if (status >= 0) { - status = rsock_connect(fd, res->ai_addr, res->ai_addrlen, - (type == INET_SOCKS), tv); + status = rsock_connect(io, res->ai_addr, res->ai_addrlen, (type == INET_SOCKS), timeout); syscall = "connect(2)"; } } if (status < 0) { error = errno; - close(fd); - arg->fd = fd = -1; + arg->io = Qnil; + rb_io_close(io); + io = Qnil; continue; - } else + } else { break; + } } + if (status < 0) { VALUE host, port; @@ -159,28 +193,1227 @@ init_inetsock_internal(VALUE v) rsock_syserr_fail_host_port(error, syscall, host, port); } - arg->fd = -1; + // Don't close the socket in `inetsock_cleanup` if we are returning it: + arg->io = Qnil; - if (type == INET_SERVER) { - status = listen(fd, SOMAXCONN); + if (type == INET_SERVER && io != Qnil) { + status = listen(rb_io_descriptor(io), SOMAXCONN); if (status < 0) { error = errno; - close(fd); + rb_io_close(io); rb_syserr_fail(error, "listen(2)"); } } /* create new instance */ - return rsock_init_sock(arg->sock, fd); + return io; +} + +#if FAST_FALLBACK_INIT_INETSOCK_IMPL == 0 + +VALUE +rsock_init_inetsock( + VALUE self, VALUE remote_host, VALUE remote_serv, + VALUE local_host, VALUE local_serv, int type, + VALUE resolv_timeout, VALUE connect_timeout, VALUE open_timeout, + VALUE _fast_fallback, VALUE _test_mode_settings) +{ + if (!NIL_P(open_timeout) && (!NIL_P(resolv_timeout) || !NIL_P(connect_timeout))) { + rb_raise(rb_eArgError, "Cannot specify open_timeout along with connect_timeout or resolv_timeout"); + } + + struct inetsock_arg arg; + arg.self = self; + arg.io = Qnil; + arg.remote.host = remote_host; + arg.remote.serv = remote_serv; + arg.remote.res = 0; + arg.local.host = local_host; + arg.local.serv = local_serv; + arg.local.res = 0; + arg.type = type; + arg.resolv_timeout = resolv_timeout; + arg.connect_timeout = connect_timeout; + arg.open_timeout = open_timeout; + return rb_ensure(init_inetsock_internal, (VALUE)&arg, + inetsock_cleanup, (VALUE)&arg); +} + +#elif FAST_FALLBACK_INIT_INETSOCK_IMPL == 1 + +#define IPV6_ENTRY_POS 0 +#define IPV4_ENTRY_POS 1 +#define RESOLUTION_ERROR 0 +#define SYSCALL_ERROR 1 + +static int +is_specified_ip_address(const char *hostname) +{ + if (!hostname) return false; + + struct in_addr ipv4addr; + struct in6_addr ipv6addr; + + return (inet_pton(AF_INET6, hostname, &ipv6addr) == 1 || + inet_pton(AF_INET, hostname, &ipv4addr) == 1); +} + +static int +is_local_port_fixed(const char *portp) +{ + if (!portp) return 0; + + char *endp; + errno = 0; + long port = strtol(portp, &endp, 10); + + if (endp == portp) return 0; + if (errno == ERANGE) return 0; + + return port > 0; +} + +struct fast_fallback_inetsock_arg +{ + VALUE self; + VALUE io; + + struct { + VALUE host, serv; + struct rb_addrinfo *res; + } remote, local; + int type; + VALUE resolv_timeout; + VALUE connect_timeout; + VALUE open_timeout; + + const char *hostp, *portp; + int *families; + int family_size; + int additional_flags; + struct fast_fallback_getaddrinfo_entry *getaddrinfo_entries[2]; + struct fast_fallback_getaddrinfo_shared *getaddrinfo_shared; + rb_fdset_t readfds, writefds; + int wait; + int connection_attempt_fds_size; + int *connection_attempt_fds; + VALUE test_mode_settings; +}; + +static struct fast_fallback_getaddrinfo_shared * +allocate_fast_fallback_getaddrinfo_shared(int family_size) +{ + struct fast_fallback_getaddrinfo_shared *shared; + + shared = (struct fast_fallback_getaddrinfo_shared *)calloc( + 1, + sizeof(struct fast_fallback_getaddrinfo_shared) + (family_size == 1 ? 0 : 2) * sizeof(struct fast_fallback_getaddrinfo_entry) + ); + + return shared; +} + +static void +allocate_fast_fallback_getaddrinfo_hints(struct addrinfo *hints, int family, int remote_addrinfo_hints, int additional_flags) +{ + MEMZERO(hints, struct addrinfo, 1); + hints->ai_family = family; + hints->ai_socktype = SOCK_STREAM; + hints->ai_protocol = IPPROTO_TCP; + hints->ai_flags = remote_addrinfo_hints; + hints->ai_flags |= additional_flags; +} + +static int* +allocate_connection_attempt_fds(int additional_capacity) +{ + int *fds = (int *)malloc(additional_capacity * sizeof(int)); + if (!fds) rb_syserr_fail(errno, "malloc(3)"); + for (int i = 0; i < additional_capacity; i++) fds[i] = -1; + return fds; +} + +static int +reallocate_connection_attempt_fds(int **fds, int current_capacity, int additional_capacity) +{ + int new_capacity = current_capacity + additional_capacity; + int *new_fds; + + new_fds = realloc(*fds, new_capacity * sizeof(int)); + if (new_fds == NULL) { + rb_syserr_fail(errno, "realloc(3)"); + } + *fds = new_fds; + + for (int i = current_capacity; i < new_capacity; i++) (*fds)[i] = -1; + return new_capacity; +} + +struct hostname_resolution_result +{ + struct addrinfo *ai; + int finished; + int has_error; +}; + +struct hostname_resolution_store +{ + struct hostname_resolution_result v6; + struct hostname_resolution_result v4; + int is_all_finished; +}; + +static int +any_addrinfos(struct hostname_resolution_store *resolution_store) +{ + return resolution_store->v6.ai || resolution_store->v4.ai; +} + +static struct timespec +current_clocktime_ts(void) +{ + struct timespec ts; + if ((clock_gettime(CLOCK_MONOTONIC, &ts)) < 0) { + rb_syserr_fail(errno, "clock_gettime(2)"); + } + return ts; +} + +static void +set_timeout_tv(struct timeval *tv, long ms, struct timespec from) +{ + long sec = ms / 1000; + long nsec = (ms % 1000) * 1000000; + long result_sec = from.tv_sec + sec; + long result_nsec = from.tv_nsec + nsec; + + result_sec += result_nsec / 1000000000; + result_nsec = result_nsec % 1000000000; + + tv->tv_sec = result_sec; + tv->tv_usec = (int)(result_nsec / 1000); +} + +static struct timeval +add_ts_to_tv(struct timeval tv, struct timespec ts) +{ + long ts_usec = ts.tv_nsec / 1000; + tv.tv_sec += ts.tv_sec; + tv.tv_usec += ts_usec; + + if (tv.tv_usec >= 1000000) { + tv.tv_sec += tv.tv_usec / 1000000; + tv.tv_usec = tv.tv_usec % 1000000; + } + + return tv; +} + +static VALUE +tv_to_seconds(struct timeval *timeout) { + if (timeout == NULL) return Qnil; + + double seconds = (double)timeout->tv_sec + (double)timeout->tv_usec / 1000000.0; + + return DBL2NUM(seconds); +} + +static int +is_infinity(struct timeval tv) +{ + // { -1, -1 } as infinity + return tv.tv_sec == -1 || tv.tv_usec == -1; +} + +static int +is_timeout_tv(struct timeval *timeout_tv, struct timespec now) { + if (!timeout_tv) return false; + if (timeout_tv->tv_sec == -1 && timeout_tv->tv_usec == -1) return false; + + struct timespec ts; + ts.tv_sec = timeout_tv->tv_sec; + ts.tv_nsec = timeout_tv->tv_usec * 1000; + + if (now.tv_sec > ts.tv_sec) return true; + if (now.tv_sec == ts.tv_sec && now.tv_nsec >= ts.tv_nsec) return true; + return false; +} + +static struct timeval * +select_expires_at( + struct hostname_resolution_store *resolution_store, + struct timeval *resolution_delay, + struct timeval *connection_attempt_delay, + struct timeval *user_specified_resolv_timeout_at, + struct timeval *user_specified_connect_timeout_at, + struct timeval *user_specified_open_timeout_at) +{ + if (any_addrinfos(resolution_store)) { + struct timeval *delay; + delay = resolution_delay ? resolution_delay : connection_attempt_delay; + + if (user_specified_open_timeout_at && + timercmp(user_specified_open_timeout_at, delay, <)) { + return user_specified_open_timeout_at; + } + return delay; + } + + if (user_specified_open_timeout_at) return user_specified_open_timeout_at; + + struct timeval *timeout = NULL; + + if (user_specified_resolv_timeout_at) { + if (is_infinity(*user_specified_resolv_timeout_at)) return NULL; + timeout = user_specified_resolv_timeout_at; + } + + if (user_specified_connect_timeout_at) { + if (is_infinity(*user_specified_connect_timeout_at)) return NULL; + if (!timeout || timercmp(user_specified_connect_timeout_at, timeout, >)) { + return user_specified_connect_timeout_at; + } + } + + return timeout; +} + +static struct timeval +tv_to_timeout(struct timeval *ends_at, struct timespec now) +{ + struct timeval delay; + struct timespec expires_at; + expires_at.tv_sec = ends_at->tv_sec; + expires_at.tv_nsec = ends_at->tv_usec * 1000; + + struct timespec diff; + diff.tv_sec = expires_at.tv_sec - now.tv_sec; + + if (expires_at.tv_nsec >= now.tv_nsec) { + diff.tv_nsec = expires_at.tv_nsec - now.tv_nsec; + } else { + diff.tv_sec -= 1; + diff.tv_nsec = (1000000000 + expires_at.tv_nsec) - now.tv_nsec; + } + + delay.tv_sec = diff.tv_sec; + delay.tv_usec = (int)diff.tv_nsec / 1000; + + return delay; +} + +static struct addrinfo * +pick_addrinfo(struct hostname_resolution_store *resolution_store, int last_family) +{ + int priority_on_v6[2] = { AF_INET6, AF_INET }; + int priority_on_v4[2] = { AF_INET, AF_INET6 }; + int *precedences = last_family == AF_INET6 ? priority_on_v4 : priority_on_v6; + struct addrinfo *selected_ai = NULL; + + for (int i = 0; i < 2; i++) { + if (precedences[i] == AF_INET6) { + selected_ai = resolution_store->v6.ai; + if (selected_ai) { + resolution_store->v6.ai = selected_ai->ai_next; + break; + } + } else { + selected_ai = resolution_store->v4.ai; + if (selected_ai) { + resolution_store->v4.ai = selected_ai->ai_next; + break; + } + } + } + return selected_ai; +} + +static void +socket_nonblock_set(int fd) +{ + int flags = fcntl(fd, F_GETFL); + + if (flags < 0) rb_syserr_fail(errno, "fcntl(2)"); + if ((flags & O_NONBLOCK) != 0) return; + + flags |= O_NONBLOCK; + + if (fcntl(fd, F_SETFL, flags) < 0) rb_syserr_fail(errno, "fcntl(2)"); + return; +} + +static int +in_progress_fds(int fds_size) +{ + return fds_size > 0; +} + +static void +remove_connection_attempt_fd(int *fds, int *fds_size, int removing_fd) +{ + int i, j; + + for (i = 0; i < *fds_size; i++) { + if (fds[i] != removing_fd) continue; + + for (j = i; j < *fds_size - 1; j++) { + fds[j] = fds[j + 1]; + } + + (*fds_size)--; + fds[*fds_size] = -1; + break; + } +} + +struct fast_fallback_error +{ + int type; + int ecode; +}; + +static VALUE +init_fast_fallback_inetsock_internal(VALUE v) +{ + struct fast_fallback_inetsock_arg *arg = (void *)v; + VALUE io = arg->io; + VALUE resolv_timeout = arg->resolv_timeout; + VALUE connect_timeout = arg->connect_timeout; + VALUE open_timeout = arg->open_timeout; + VALUE test_mode_settings = arg->test_mode_settings; + struct addrinfo *remote_ai = NULL, *local_ai = NULL; + int connected_fd = -1, status = 0, local_status = 0; + int remote_addrinfo_hints = 0; + struct fast_fallback_error last_error = { 0, 0 }; + const char *syscall = 0; + VALUE host, serv; + + #ifdef HAVE_CONST_AI_ADDRCONFIG + remote_addrinfo_hints |= AI_ADDRCONFIG; + #endif + + pthread_t threads[arg->family_size]; + char resolved_type[2]; + ssize_t resolved_type_size; + int hostname_resolution_waiter = -1, hostname_resolution_notifier = -1; + int pipefd[2]; + + int nfds = 0; + struct timeval *ends_at = NULL; + struct timeval delay = (struct timeval){ -1, -1 }; + struct timeval *delay_p = NULL; + + struct hostname_resolution_store resolution_store; + resolution_store.is_all_finished = false; + resolution_store.v6.ai = NULL; + resolution_store.v6.finished = false; + resolution_store.v6.has_error = false; + resolution_store.v4.ai = NULL; + resolution_store.v4.finished = false; + resolution_store.v4.has_error = false; + + int last_family = 0; + int additional_capacity = 10; + int current_capacity = additional_capacity; + arg->connection_attempt_fds = allocate_connection_attempt_fds(additional_capacity); + arg->connection_attempt_fds_size = 0; + + struct timeval resolution_delay_storage; + struct timeval *resolution_delay_expires_at = NULL; + struct timeval connection_attempt_delay_strage; + struct timeval *connection_attempt_delay_expires_at = NULL; + struct timeval user_specified_resolv_timeout_storage; + struct timeval *user_specified_resolv_timeout_at = NULL; + struct timeval user_specified_connect_timeout_storage; + struct timeval *user_specified_connect_timeout_at = NULL; + struct timeval user_specified_open_timeout_storage; + struct timeval *user_specified_open_timeout_at = NULL; + struct timespec now = current_clocktime_ts(); + VALUE starts_at = current_clocktime(); + + if (!NIL_P(open_timeout)) { + struct timeval open_timeout_tv = rb_time_interval(open_timeout); + user_specified_open_timeout_storage = add_ts_to_tv(open_timeout_tv, now); + user_specified_open_timeout_at = &user_specified_open_timeout_storage; + } + + /* start of hostname resolution */ + if (arg->family_size == 1) { + arg->wait = -1; + arg->getaddrinfo_shared = NULL; + + int family = arg->families[0]; + VALUE t = NIL_P(open_timeout) ? resolv_timeout : open_timeout; + + arg->remote.res = rsock_addrinfo( + arg->remote.host, + arg->remote.serv, + family, + SOCK_STREAM, + 0, + t + ); + + if (family == AF_INET6) { + resolution_store.v6.ai = arg->remote.res->ai; + resolution_store.v6.finished = true; + resolution_store.v4.finished = true; + } else if (family == AF_INET) { + resolution_store.v4.ai = arg->remote.res->ai; + resolution_store.v4.finished = true; + resolution_store.v6.finished = true; + } + resolution_store.is_all_finished = true; + } else { + if (pipe(pipefd) != 0) rb_syserr_fail(errno, "pipe(2)"); + hostname_resolution_waiter = pipefd[0]; + int waiter_flags = fcntl(hostname_resolution_waiter, F_GETFL, 0); + if (waiter_flags < 0) rb_syserr_fail(errno, "fcntl(2)"); + if ((fcntl(hostname_resolution_waiter, F_SETFL, waiter_flags | O_NONBLOCK)) < 0) { + rb_syserr_fail(errno, "fcntl(2)"); + } + arg->wait = hostname_resolution_waiter; + hostname_resolution_notifier = pipefd[1]; + + arg->getaddrinfo_shared = allocate_fast_fallback_getaddrinfo_shared(arg->family_size); + if (!arg->getaddrinfo_shared) rb_syserr_fail(errno, "calloc(3)"); + + rb_nativethread_lock_initialize(&arg->getaddrinfo_shared->lock); + arg->getaddrinfo_shared->notify = hostname_resolution_notifier; + + arg->getaddrinfo_shared->node = arg->hostp ? ruby_strdup(arg->hostp) : NULL; + arg->getaddrinfo_shared->service = arg->portp ? ruby_strdup(arg->portp) : NULL; + arg->getaddrinfo_shared->refcount = arg->family_size + 1; + + for (int i = 0; i < arg->family_size; i++) { + arg->getaddrinfo_entries[i] = &arg->getaddrinfo_shared->getaddrinfo_entries[i]; + arg->getaddrinfo_entries[i]->shared = arg->getaddrinfo_shared; + + struct addrinfo getaddrinfo_hints[arg->family_size]; + + allocate_fast_fallback_getaddrinfo_hints( + &getaddrinfo_hints[i], + arg->families[i], + remote_addrinfo_hints, + arg->additional_flags + ); + + arg->getaddrinfo_entries[i]->hints = getaddrinfo_hints[i]; + arg->getaddrinfo_entries[i]->ai = NULL; + arg->getaddrinfo_entries[i]->family = arg->families[i]; + arg->getaddrinfo_entries[i]->refcount = 2; + arg->getaddrinfo_entries[i]->has_syserr = false; + arg->getaddrinfo_entries[i]->test_sleep_ms = 0; + arg->getaddrinfo_entries[i]->test_ecode = 0; + + /* for testing HEv2 */ + if (!NIL_P(test_mode_settings) && RB_TYPE_P(test_mode_settings, T_HASH)) { + const char *family_sym = arg->families[i] == AF_INET6 ? "ipv6" : "ipv4"; + + VALUE test_delay_setting = rb_hash_aref(test_mode_settings, ID2SYM(rb_intern("delay"))); + if (!NIL_P(test_delay_setting)) { + VALUE rb_test_delay_ms = rb_hash_aref(test_delay_setting, ID2SYM(rb_intern(family_sym))); + long test_delay_ms = NIL_P(rb_test_delay_ms) ? 0 : rb_test_delay_ms; + arg->getaddrinfo_entries[i]->test_sleep_ms = test_delay_ms; + } + + VALUE test_error_setting = rb_hash_aref(test_mode_settings, ID2SYM(rb_intern("error"))); + if (!NIL_P(test_error_setting)) { + VALUE rb_test_ecode = rb_hash_aref(test_error_setting, ID2SYM(rb_intern(family_sym))); + if (!NIL_P(rb_test_ecode)) { + arg->getaddrinfo_entries[i]->test_ecode = NUM2INT(rb_test_ecode); + } + } + } + + if (raddrinfo_pthread_create(&threads[i], fork_safe_do_fast_fallback_getaddrinfo, arg->getaddrinfo_entries[i]) != 0) { + rsock_raise_resolution_error("getaddrinfo(3)", EAI_AGAIN); + } + } + + if (NIL_P(resolv_timeout)) { + user_specified_resolv_timeout_storage = (struct timeval){ -1, -1 }; + } else { + struct timeval resolv_timeout_tv = rb_time_interval(resolv_timeout); + user_specified_resolv_timeout_storage = add_ts_to_tv(resolv_timeout_tv, now); + } + user_specified_resolv_timeout_at = &user_specified_resolv_timeout_storage; + } + + while (true) { + /* start of connection */ + if (any_addrinfos(&resolution_store) && + !resolution_delay_expires_at && + !connection_attempt_delay_expires_at) { + while ((remote_ai = pick_addrinfo(&resolution_store, last_family))) { + int fd = -1; + + #if !defined(INET6) && defined(AF_INET6) + if (remote_ai->ai_family == AF_INET6) { + if (any_addrinfos(&resolution_store)) continue; + if (!in_progress_fds(arg->connection_attempt_fds_size)) break; + if (resolution_store.is_all_finished) break; + + if (local_status < 0) { + host = arg->local.host; + serv = arg->local.serv; + } else { + host = arg->remote.host; + serv = arg->remote.serv; + } + if (last_error.type == RESOLUTION_ERROR) { + rsock_raise_resolution_error(syscall, last_error.ecode); + } else { + rsock_syserr_fail_host_port(last_error.ecode, syscall, host, serv); + } + } + #endif + + local_ai = NULL; + + if (arg->local.res) { + for (local_ai = arg->local.res->ai; local_ai; local_ai = local_ai->ai_next) { + if (local_ai->ai_family == remote_ai->ai_family) break; + } + if (!local_ai) { + if (any_addrinfos(&resolution_store)) continue; + if (in_progress_fds(arg->connection_attempt_fds_size)) break; + if (!resolution_store.is_all_finished) break; + + /* Use a different family local address if no choice, this + * will cause EAFNOSUPPORT. */ + rsock_syserr_fail_host_port(EAFNOSUPPORT, syscall, arg->local.host, arg->local.serv); + } + } + + status = rsock_socket(remote_ai->ai_family, remote_ai->ai_socktype, remote_ai->ai_protocol); + syscall = "socket(2)"; + + if (status < 0) { + last_error.type = SYSCALL_ERROR; + last_error.ecode = errno; + + if (any_addrinfos(&resolution_store)) continue; + if (in_progress_fds(arg->connection_attempt_fds_size)) break; + if (!resolution_store.is_all_finished) break; + + if (local_status < 0) { + host = arg->local.host; + serv = arg->local.serv; + } else { + host = arg->remote.host; + serv = arg->remote.serv; + } + if (last_error.type == RESOLUTION_ERROR) { + rsock_raise_resolution_error(syscall, last_error.ecode); + } else { + rsock_syserr_fail_host_port(last_error.ecode, syscall, host, serv); + } + } + + fd = status; + + if (local_ai) { + #if !defined(_WIN32) && !defined(__CYGWIN__) + status = 1; + if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&status, (socklen_t)sizeof(status))) < 0) { + rb_syserr_fail(errno, "setsockopt(2)"); + } + #endif + status = bind(fd, local_ai->ai_addr, local_ai->ai_addrlen); + local_status = status; + syscall = "bind(2)"; + + if (status < 0) { + last_error.type = SYSCALL_ERROR; + last_error.ecode = errno; + close(fd); + + if (any_addrinfos(&resolution_store)) continue; + if (in_progress_fds(arg->connection_attempt_fds_size)) break; + if (!resolution_store.is_all_finished) break; + + if (local_status < 0) { + host = arg->local.host; + serv = arg->local.serv; + } else { + host = arg->remote.host; + serv = arg->remote.serv; + } + if (last_error.type == RESOLUTION_ERROR) { + rsock_raise_resolution_error(syscall, last_error.ecode); + } else { + rsock_syserr_fail_host_port(last_error.ecode, syscall, host, serv); + } + } + } + + syscall = "connect(2)"; + + if (any_addrinfos(&resolution_store) || + in_progress_fds(arg->connection_attempt_fds_size) || + !resolution_store.is_all_finished) { + socket_nonblock_set(fd); + status = connect(fd, remote_ai->ai_addr, remote_ai->ai_addrlen); + last_family = remote_ai->ai_family; + } else { + VALUE timeout = Qnil; + + if (!NIL_P(open_timeout)) { + VALUE elapsed = rb_funcall(current_clocktime(), '-', 1, starts_at); + timeout = rb_funcall(open_timeout, '-', 1, elapsed); + + if (rb_funcall(timeout, '<', 1, INT2FIX(0)) == Qtrue) { + rsock_raise_user_specified_timeout(NULL, arg->remote.host, arg->remote.serv); + } + } + if (NIL_P(timeout)) { + if (!NIL_P(connect_timeout)) { + user_specified_connect_timeout_storage = rb_time_interval(connect_timeout); + user_specified_connect_timeout_at = &user_specified_connect_timeout_storage; + } + timeout = + (user_specified_connect_timeout_at && is_infinity(*user_specified_connect_timeout_at)) ? + Qnil : tv_to_seconds(user_specified_connect_timeout_at); + } + + io = arg->io = rsock_init_sock(arg->self, fd); + status = rsock_connect(io, remote_ai->ai_addr, remote_ai->ai_addrlen, 0, timeout); + } + + if (status == 0) { + connected_fd = fd; + break; + } + + if (errno == EINPROGRESS) { + if (current_capacity == arg->connection_attempt_fds_size) { + current_capacity = reallocate_connection_attempt_fds( + &arg->connection_attempt_fds, + current_capacity, + additional_capacity + ); + } + arg->connection_attempt_fds[arg->connection_attempt_fds_size] = fd; + (arg->connection_attempt_fds_size)++; + + set_timeout_tv(&connection_attempt_delay_strage, 250, now); + connection_attempt_delay_expires_at = &connection_attempt_delay_strage; + + if (!any_addrinfos(&resolution_store)) { + if (NIL_P(connect_timeout)) { + user_specified_connect_timeout_storage = (struct timeval){ -1, -1 }; + } else { + struct timeval connect_timeout_tv = rb_time_interval(connect_timeout); + user_specified_connect_timeout_storage = add_ts_to_tv(connect_timeout_tv, now); + } + user_specified_connect_timeout_at = &user_specified_connect_timeout_storage; + } + + break; + } + + last_error.type = SYSCALL_ERROR; + last_error.ecode = errno; + + if (NIL_P(io)) { + close(fd); + } else { + rb_io_close(io); + } + + if (any_addrinfos(&resolution_store)) continue; + if (in_progress_fds(arg->connection_attempt_fds_size)) break; + if (!resolution_store.is_all_finished) break; + + if (local_status < 0) { + host = arg->local.host; + serv = arg->local.serv; + } else { + host = arg->remote.host; + serv = arg->remote.serv; + } + if (last_error.type == RESOLUTION_ERROR) { + rsock_raise_resolution_error(syscall, last_error.ecode); + } else { + rsock_syserr_fail_host_port(last_error.ecode, syscall, host, serv); + } + } + } + + if (connected_fd >= 0) break; + + ends_at = select_expires_at( + &resolution_store, + resolution_delay_expires_at, + connection_attempt_delay_expires_at, + user_specified_resolv_timeout_at, + user_specified_connect_timeout_at, + user_specified_open_timeout_at + ); + if (ends_at) { + delay = tv_to_timeout(ends_at, now); + delay_p = &delay; + } else { + if (((resolution_store.v6.finished && !resolution_store.v4.finished) || + (resolution_store.v4.finished && !resolution_store.v6.finished)) && + !any_addrinfos(&resolution_store) && + !in_progress_fds(arg->connection_attempt_fds_size)) { + /* A limited timeout is introduced to prevent select(2) from hanging when it is exclusively + * waiting for name resolution and write(2) failure occurs in a child thread. */ + delay.tv_sec = 0; + delay.tv_usec = 50000; + delay_p = &delay; + } else { + delay_p = NULL; + } + } + + nfds = 0; + rb_fd_zero(&arg->writefds); + if (in_progress_fds(arg->connection_attempt_fds_size)) { + int n = 0; + for (int i = 0; i < arg->connection_attempt_fds_size; i++) { + int cfd = arg->connection_attempt_fds[i]; + if (cfd < 0) continue; + if (cfd > n) n = cfd; + rb_fd_set(cfd, &arg->writefds); + } + if (n > 0) n++; + nfds = n; + } + + rb_fd_zero(&arg->readfds); + if (arg->family_size > 1) { + rb_fd_set(hostname_resolution_waiter, &arg->readfds); + + if ((hostname_resolution_waiter + 1) > nfds) { + nfds = hostname_resolution_waiter + 1; + } + } + + status = rb_thread_fd_select(nfds, &arg->readfds, &arg->writefds, NULL, delay_p); + + now = current_clocktime_ts(); + if (is_timeout_tv(resolution_delay_expires_at, now)) { + resolution_delay_expires_at = NULL; + } + if (is_timeout_tv(connection_attempt_delay_expires_at, now)) { + connection_attempt_delay_expires_at = NULL; + } + + if (status < 0 && (errno && errno != EINTR)) rb_syserr_fail(errno, "select(2)"); + + if (status > 0) { + /* check for connection */ + if (in_progress_fds(arg->connection_attempt_fds_size)) { + for (int i = 0; i < arg->connection_attempt_fds_size; i++) { + int fd = arg->connection_attempt_fds[i]; + if (fd < 0 || !rb_fd_isset(fd, &arg->writefds)) continue; + + int err; + socklen_t len = sizeof(err); + + status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len); + + if (status < 0) { + last_error.type = SYSCALL_ERROR; + last_error.ecode = errno; + close(fd); + + if (any_addrinfos(&resolution_store)) continue; + if (in_progress_fds(arg->connection_attempt_fds_size)) break; + if (!resolution_store.is_all_finished) break; + + if (local_status < 0) { + host = arg->local.host; + serv = arg->local.serv; + } else { + host = arg->remote.host; + serv = arg->remote.serv; + } + if (last_error.type == RESOLUTION_ERROR) { + rsock_raise_resolution_error(syscall, last_error.ecode); + } else { + rsock_syserr_fail_host_port(last_error.ecode, syscall, host, serv); + } + } + + if (err == 0) { /* success */ + remove_connection_attempt_fd( + arg->connection_attempt_fds, + &arg->connection_attempt_fds_size, + fd + ); + connected_fd = fd; + break; + } else { /* fail */ + close(fd); + remove_connection_attempt_fd( + arg->connection_attempt_fds, + &arg->connection_attempt_fds_size, + fd + ); + last_error.type = SYSCALL_ERROR; + last_error.ecode = err; + } + } + + if (connected_fd >= 0) break; + + if (!in_progress_fds(arg->connection_attempt_fds_size)) { + if (!any_addrinfos(&resolution_store) && resolution_store.is_all_finished) { + if (local_status < 0) { + host = arg->local.host; + serv = arg->local.serv; + } else { + host = arg->remote.host; + serv = arg->remote.serv; + } + if (last_error.type == RESOLUTION_ERROR) { + rsock_raise_resolution_error(syscall, last_error.ecode); + } else { + rsock_syserr_fail_host_port(last_error.ecode, syscall, host, serv); + } + } + connection_attempt_delay_expires_at = NULL; + user_specified_connect_timeout_at = NULL; + } + } + + /* check for hostname resolution */ + if (!resolution_store.is_all_finished && rb_fd_isset(hostname_resolution_waiter, &arg->readfds)) { + while (true) { + resolved_type_size = read( + hostname_resolution_waiter, + resolved_type, + sizeof(resolved_type) - 1 + ); + + if (resolved_type_size > 0) { + resolved_type[resolved_type_size] = '\0'; + + if (resolved_type[0] == IPV6_HOSTNAME_RESOLVED) { + resolution_store.v6.finished = true; + + if (arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err && + arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err != EAI_ADDRFAMILY) { + if (!resolution_store.v4.finished || resolution_store.v4.has_error) { + last_error.type = RESOLUTION_ERROR; + last_error.ecode = arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err; + syscall = "getaddrinfo(3)"; + } + resolution_store.v6.has_error = true; + } else { + resolution_store.v6.ai = arg->getaddrinfo_entries[IPV6_ENTRY_POS]->ai; + } + if (resolution_store.v4.finished) { + resolution_store.is_all_finished = true; + resolution_delay_expires_at = NULL; + user_specified_resolv_timeout_at = NULL; + break; + } + } else if (resolved_type[0] == IPV4_HOSTNAME_RESOLVED) { + resolution_store.v4.finished = true; + + if (arg->getaddrinfo_entries[IPV4_ENTRY_POS]->err) { + if (!resolution_store.v6.finished || resolution_store.v6.has_error) { + last_error.type = RESOLUTION_ERROR; + last_error.ecode = arg->getaddrinfo_entries[IPV4_ENTRY_POS]->err; + syscall = "getaddrinfo(3)"; + } + resolution_store.v4.has_error = true; + } else { + resolution_store.v4.ai = arg->getaddrinfo_entries[IPV4_ENTRY_POS]->ai; + } + + if (resolution_store.v6.finished) { + resolution_store.is_all_finished = true; + resolution_delay_expires_at = NULL; + user_specified_resolv_timeout_at = NULL; + break; + } + } else { + /* Retry to read from hostname_resolution_waiter */ + } + } else if (resolved_type_size < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + errno = 0; + break; + } else { + /* Retry to read from hostname_resolution_waiter */ + } + + if (!resolution_store.v6.finished && + resolution_store.v4.finished && + !resolution_store.v4.has_error) { + set_timeout_tv(&resolution_delay_storage, 50, now); + resolution_delay_expires_at = &resolution_delay_storage; + } + } + } + + status = 0; + } + + /* For cases where write(2) fails in child threads */ + if (!resolution_store.is_all_finished) { + if (!resolution_store.v6.finished && arg->getaddrinfo_entries[IPV6_ENTRY_POS]->has_syserr) { + resolution_store.v6.finished = true; + + if (arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err) { + if (!resolution_store.v4.finished || resolution_store.v4.has_error) { + last_error.type = RESOLUTION_ERROR; + last_error.ecode = arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err; + syscall = "getaddrinfo(3)"; + } + resolution_store.v6.has_error = true; + } else { + resolution_store.v6.ai = arg->getaddrinfo_entries[IPV6_ENTRY_POS]->ai; + } + + if (resolution_store.v4.finished) { + resolution_store.is_all_finished = true; + resolution_delay_expires_at = NULL; + user_specified_resolv_timeout_at = NULL; + } + } + if (!resolution_store.v4.finished && arg->getaddrinfo_entries[IPV4_ENTRY_POS]->has_syserr) { + resolution_store.v4.finished = true; + + if (arg->getaddrinfo_entries[IPV4_ENTRY_POS]->err) { + if (!resolution_store.v6.finished || resolution_store.v6.has_error) { + last_error.type = RESOLUTION_ERROR; + last_error.ecode = arg->getaddrinfo_entries[IPV4_ENTRY_POS]->err; + syscall = "getaddrinfo(3)"; + } + resolution_store.v4.has_error = true; + } else { + resolution_store.v4.ai = arg->getaddrinfo_entries[IPV4_ENTRY_POS]->ai; + } + + if (resolution_store.v6.finished) { + resolution_store.is_all_finished = true; + resolution_delay_expires_at = NULL; + user_specified_resolv_timeout_at = NULL; + } else { + set_timeout_tv(&resolution_delay_storage, 50, now); + resolution_delay_expires_at = &resolution_delay_storage; + } + } + } + + if (is_timeout_tv(user_specified_open_timeout_at, now)) { + rsock_raise_user_specified_timeout(NULL, arg->remote.host, arg->remote.serv); + } + + if (!any_addrinfos(&resolution_store)) { + if (!in_progress_fds(arg->connection_attempt_fds_size) && + resolution_store.is_all_finished) { + if (local_status < 0) { + host = arg->local.host; + serv = arg->local.serv; + } else { + host = arg->remote.host; + serv = arg->remote.serv; + } + if (last_error.type == RESOLUTION_ERROR) { + rsock_raise_resolution_error(syscall, last_error.ecode); + } else { + rsock_syserr_fail_host_port(last_error.ecode, syscall, host, serv); + } + } + + if ((is_timeout_tv(user_specified_resolv_timeout_at, now) || + resolution_store.is_all_finished) && + (is_timeout_tv(user_specified_connect_timeout_at, now) || + !in_progress_fds(arg->connection_attempt_fds_size))) { + rsock_raise_user_specified_timeout(NULL, arg->remote.host, arg->remote.serv); + } + } + } + + if (NIL_P(arg->io)) { + /* create new instance */ + arg->io = rsock_init_sock(arg->self, connected_fd); + } + + return arg->io; +} + +static VALUE +fast_fallback_inetsock_cleanup(VALUE v) +{ + struct fast_fallback_inetsock_arg *arg = (void *)v; + struct fast_fallback_getaddrinfo_shared *getaddrinfo_shared = arg->getaddrinfo_shared; + + if (arg->remote.res) { + rb_freeaddrinfo(arg->remote.res); + arg->remote.res = 0; + } + if (arg->local.res) { + rb_freeaddrinfo(arg->local.res); + arg->local.res = 0; + } + + if (arg->wait != -1) close(arg->wait); + + if (getaddrinfo_shared) { + if (getaddrinfo_shared->notify != -1) close(getaddrinfo_shared->notify); + getaddrinfo_shared->notify = -1; + + int shared_need_free = 0; + struct addrinfo *ais[arg->family_size]; + for (int i = 0; i < arg->family_size; i++) ais[i] = NULL; + + rb_nativethread_lock_lock(&getaddrinfo_shared->lock); + { + for (int i = 0; i < arg->family_size; i++) { + struct fast_fallback_getaddrinfo_entry *getaddrinfo_entry = arg->getaddrinfo_entries[i]; + + if (!getaddrinfo_entry) continue; + + if (--(getaddrinfo_entry->refcount) == 0) { + ais[i] = getaddrinfo_entry->ai; + getaddrinfo_entry->ai = NULL; + } + } + if (--(getaddrinfo_shared->refcount) == 0) { + shared_need_free = 1; + } + } + rb_nativethread_lock_unlock(&getaddrinfo_shared->lock); + + for (int i = 0; i < arg->family_size; i++) { + if (ais[i]) freeaddrinfo(ais[i]); + } + if (getaddrinfo_shared && shared_need_free) { + free_fast_fallback_getaddrinfo_shared(&getaddrinfo_shared); + } + } + + int connection_attempt_fd; + + for (int i = 0; i < arg->connection_attempt_fds_size; i++) { + connection_attempt_fd = arg->connection_attempt_fds[i]; + + if (connection_attempt_fd >= 0) { + int error = 0; + socklen_t len = sizeof(error); + getsockopt(connection_attempt_fd, SOL_SOCKET, SO_ERROR, &error, &len); + if (error == 0) shutdown(connection_attempt_fd, SHUT_RDWR); + close(connection_attempt_fd); + } + } + + if (arg->readfds.fdset) rb_fd_term(&arg->readfds); + if (arg->writefds.fdset) rb_fd_term(&arg->writefds); + + if (arg->connection_attempt_fds) { + free(arg->connection_attempt_fds); + arg->connection_attempt_fds = NULL; + } + + return Qnil; } VALUE -rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv, - VALUE local_host, VALUE local_serv, int type, - VALUE resolv_timeout, VALUE connect_timeout) +rsock_init_inetsock( + VALUE self, VALUE remote_host, VALUE remote_serv, + VALUE local_host, VALUE local_serv, int type, + VALUE resolv_timeout, VALUE connect_timeout, VALUE open_timeout, + VALUE fast_fallback, VALUE test_mode_settings) { + if (!NIL_P(open_timeout) && (!NIL_P(resolv_timeout) || !NIL_P(connect_timeout))) { + rb_raise(rb_eArgError, "Cannot specify open_timeout along with connect_timeout or resolv_timeout"); + } + + if (type == INET_CLIENT && FAST_FALLBACK_INIT_INETSOCK_IMPL == 1 && RTEST(fast_fallback)) { + struct rb_addrinfo *local_res = NULL; + char *hostp, *portp, *local_portp; + char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV], local_pbuf[NI_MAXSERV]; + int additional_flags = 0; + int local_flags = 0; + hostp = raddrinfo_host_str(remote_host, hbuf, sizeof(hbuf), &additional_flags); + portp = raddrinfo_port_str(remote_serv, pbuf, sizeof(pbuf), &additional_flags); + local_portp = raddrinfo_port_str(local_serv, local_pbuf, sizeof(local_pbuf), &local_flags); + + if (!is_specified_ip_address(hostp) && !is_local_port_fixed(local_portp)) { + int target_families[2] = { 0, 0 }; + int resolving_family_size = 0; + + /* + * Maybe also accept a local address + */ + if (!NIL_P(local_host) || !NIL_P(local_serv)) { + VALUE t = NIL_P(open_timeout) ? resolv_timeout : open_timeout; + local_res = rsock_addrinfo( + local_host, + local_serv, + AF_UNSPEC, + SOCK_STREAM, + 0, + t + ); + + struct addrinfo *tmp_p = local_res->ai; + for (; tmp_p != NULL; tmp_p = tmp_p->ai_next) { + if (target_families[0] == 0 && tmp_p->ai_family == AF_INET6) { + target_families[0] = AF_INET6; + resolving_family_size++; + } + if (target_families[1] == 0 && tmp_p->ai_family == AF_INET) { + target_families[1] = AF_INET; + resolving_family_size++; + } + } + } else { + resolving_family_size = 2; + target_families[0] = AF_INET6; + target_families[1] = AF_INET; + } + + struct fast_fallback_inetsock_arg fast_fallback_arg; + memset(&fast_fallback_arg, 0, sizeof(fast_fallback_arg)); + + fast_fallback_arg.self = self; + fast_fallback_arg.io = Qnil; + fast_fallback_arg.remote.host = remote_host; + fast_fallback_arg.remote.serv = remote_serv; + fast_fallback_arg.remote.res = 0; + fast_fallback_arg.local.host = local_host; + fast_fallback_arg.local.serv = local_serv; + fast_fallback_arg.local.res = local_res; + fast_fallback_arg.type = type; + fast_fallback_arg.resolv_timeout = resolv_timeout; + fast_fallback_arg.connect_timeout = connect_timeout; + fast_fallback_arg.open_timeout = open_timeout; + fast_fallback_arg.hostp = hostp; + fast_fallback_arg.portp = portp; + fast_fallback_arg.additional_flags = additional_flags; + + int resolving_families[resolving_family_size]; + int resolving_family_index = 0; + for (int i = 0; 2 > i; i++) { + if (target_families[i] != 0) { + resolving_families[resolving_family_index] = target_families[i]; + resolving_family_index++; + } + } + fast_fallback_arg.families = resolving_families; + fast_fallback_arg.family_size = resolving_family_size; + fast_fallback_arg.test_mode_settings = test_mode_settings; + + rb_fd_init(&fast_fallback_arg.readfds); + rb_fd_init(&fast_fallback_arg.writefds); + + return rb_ensure(init_fast_fallback_inetsock_internal, (VALUE)&fast_fallback_arg, + fast_fallback_inetsock_cleanup, (VALUE)&fast_fallback_arg); + } + } + struct inetsock_arg arg; - arg.sock = sock; + arg.self = self; + arg.io = Qnil; arg.remote.host = remote_host; arg.remote.serv = remote_serv; arg.remote.res = 0; @@ -188,13 +1421,16 @@ rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv, arg.local.serv = local_serv; arg.local.res = 0; arg.type = type; - arg.fd = -1; arg.resolv_timeout = resolv_timeout; arg.connect_timeout = connect_timeout; + arg.open_timeout = open_timeout; + return rb_ensure(init_inetsock_internal, (VALUE)&arg, inetsock_cleanup, (VALUE)&arg); } +#endif + static ID id_numeric, id_hostname; int @@ -281,16 +1517,13 @@ ip_inspect(VALUE sock) static VALUE ip_addr(int argc, VALUE *argv, VALUE sock) { - rb_io_t *fptr; union_sockaddr addr; socklen_t len = (socklen_t)sizeof addr; int norevlookup; - GetOpenFile(sock, fptr); - if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup)) - norevlookup = fptr->mode & FMODE_NOREVLOOKUP; - if (getsockname(fptr->fd, &addr.addr, &len) < 0) + norevlookup = rb_io_mode(sock) & FMODE_NOREVLOOKUP; + if (getsockname(rb_io_descriptor(sock), &addr.addr, &len) < 0) rb_sys_fail("getsockname(2)"); return rsock_ipaddr(&addr.addr, len, norevlookup); } @@ -322,16 +1555,13 @@ ip_addr(int argc, VALUE *argv, VALUE sock) static VALUE ip_peeraddr(int argc, VALUE *argv, VALUE sock) { - rb_io_t *fptr; union_sockaddr addr; socklen_t len = (socklen_t)sizeof addr; int norevlookup; - GetOpenFile(sock, fptr); - if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup)) - norevlookup = fptr->mode & FMODE_NOREVLOOKUP; - if (getpeername(fptr->fd, &addr.addr, &len) < 0) + norevlookup = rb_io_mode(sock) & FMODE_NOREVLOOKUP; + if (getpeername(rb_io_descriptor(sock), &addr.addr, &len) < 0) rb_sys_fail("getpeername(2)"); return rsock_ipaddr(&addr.addr, len, norevlookup); } @@ -379,7 +1609,7 @@ static VALUE ip_s_getaddress(VALUE obj, VALUE host) { union_sockaddr addr; - struct rb_addrinfo *res = rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, 0); + struct rb_addrinfo *res = rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, 0, Qnil); socklen_t len = res->ai->ai_addrlen; /* just take the first one */ diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb index de542223c6..36fcceaee9 100644 --- a/ext/socket/lib/socket.rb +++ b/ext/socket/lib/socket.rb @@ -62,7 +62,7 @@ class Addrinfo break when :wait_writable sock.wait_writable(timeout) or - raise Errno::ETIMEDOUT, 'user specified timeout' + raise Errno::ETIMEDOUT, "user specified timeout for #{self.ip_address}:#{self.ip_port}" end while true else sock.connect(self) @@ -599,19 +599,54 @@ class Socket < BasicSocket __accept_nonblock(exception) end + # :stopdoc: + RESOLUTION_DELAY = 0.05 + private_constant :RESOLUTION_DELAY + + CONNECTION_ATTEMPT_DELAY = 0.25 + private_constant :CONNECTION_ATTEMPT_DELAY + + ADDRESS_FAMILIES = { + ipv6: Socket::AF_INET6, + ipv4: Socket::AF_INET + }.freeze + private_constant :ADDRESS_FAMILIES + + HOSTNAME_RESOLUTION_QUEUE_UPDATED = 0 + private_constant :HOSTNAME_RESOLUTION_QUEUE_UPDATED + + IPV6_ADDRESS_FORMAT = /\A(?i:(?:(?:[0-9A-F]{1,4}:){7}(?:[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){6}(?:[0-9A-F]{1,4}|:(?:[0-9A-F]{1,4}:){1,5}[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){5}(?:(?::[0-9A-F]{1,4}){1,2}|:(?:[0-9A-F]{1,4}:){1,4}[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){4}(?:(?::[0-9A-F]{1,4}){1,3}|:(?:[0-9A-F]{1,4}:){1,3}[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){3}(?:(?::[0-9A-F]{1,4}){1,4}|:(?:[0-9A-F]{1,4}:){1,2}[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){2}(?:(?::[0-9A-F]{1,4}){1,5}|:(?:[0-9A-F]{1,4}:)[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){1}(?:(?::[0-9A-F]{1,4}){1,6}|:(?:[0-9A-F]{1,4}:){0,5}[0-9A-F]{1,4}|:)|(?:::(?:[0-9A-F]{1,4}:){0,7}[0-9A-F]{1,4}|::)))(?:%.+)?\z/ + private_constant :IPV6_ADDRESS_FORMAT + # :startdoc: + # :call-seq: # Socket.tcp(host, port, local_host=nil, local_port=nil, [opts]) {|socket| ... } # Socket.tcp(host, port, local_host=nil, local_port=nil, [opts]) # # creates a new socket object connected to host:port using TCP/IP. # + # Starting from Ruby 3.4, this method operates according to the + # Happy Eyeballs Version 2 ({RFC 8305}[https://datatracker.ietf.org/doc/html/rfc8305]) + # algorithm by default. + # + # For details on Happy Eyeballs Version 2, + # see {Socket.tcp_fast_fallback=}[rdoc-ref:Socket.tcp_fast_fallback=]. + # + # To make it behave the same as in Ruby 3.3 and earlier, + # explicitly specify the option fast_fallback:false. + # Or, setting Socket.tcp_fast_fallback=false will disable + # Happy Eyeballs Version 2 not only for this method but for all Socket globally. + # # If local_host:local_port is given, # the socket is bound to it. # # The optional last argument _opts_ is options represented by a hash. # _opts_ may have following options: # - # [:connect_timeout] specify the timeout in seconds. + # [:resolv_timeout] Specifies the timeout in seconds from when the hostname resolution starts. + # [:connect_timeout] This method sequentially attempts connecting to all candidate destination addresses.<br>The +connect_timeout+ specifies the timeout in seconds from the start of the connection attempt to the last candidate.<br>By default, all connection attempts continue until the timeout occurs.<br>When +fast_fallback:false+ is explicitly specified,<br>a timeout is set for each connection attempt and any connection attempt that exceeds its timeout will be canceled. + # [:open_timeout] Specifies the timeout in seconds from the start of the method execution.<br>If this timeout is reached while there are still addresses that have not yet been attempted for connection, no further attempts will be made.<br>If this option is specified together with other timeout options, an +ArgumentError+ will be raised. + # [:fast_fallback] Enables the Happy Eyeballs Version 2 algorithm (enabled by default). # # If a block is given, the block is called with the socket. # The value of the block is returned. @@ -624,17 +659,296 @@ class Socket < BasicSocket # sock.close_write # puts sock.read # } - # - def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil) # :yield: socket + def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil, open_timeout: nil, fast_fallback: tcp_fast_fallback, &) # :yield: socket + if open_timeout && (connect_timeout || resolv_timeout) + raise ArgumentError, "Cannot specify open_timeout along with connect_timeout or resolv_timeout" + end + + sock = if fast_fallback && !(host && ip_address?(host)) && !(local_port && local_port.to_i != 0) + tcp_with_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:, open_timeout:) + else + tcp_without_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:, open_timeout:) + end + + if block_given? + begin + yield sock + ensure + sock.close + end + else + sock + end + end + + # :stopdoc: + def self.tcp_with_fast_fallback(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil, open_timeout: nil) + if local_host || local_port + local_addrinfos = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, timeout: open_timeout || resolv_timeout) + resolving_family_names = local_addrinfos.map { |lai| ADDRESS_FAMILIES.key(lai.afamily) }.uniq + else + local_addrinfos = [] + resolving_family_names = ADDRESS_FAMILIES.keys + end + + hostname_resolution_threads = [] + resolution_store = HostnameResolutionStore.new(resolving_family_names) + connecting_sockets = {} + is_windows_environment ||= (RUBY_PLATFORM =~ /mswin|mingw|cygwin/) + + now = current_clock_time + starts_at = now + resolution_delay_expires_at = nil + connection_attempt_delay_expires_at = nil + user_specified_connect_timeout_at = nil + user_specified_open_timeout_at = open_timeout ? now + open_timeout : nil + last_error = nil + last_error_from_thread = false + + if resolving_family_names.size == 1 + family_name = resolving_family_names.first + addrinfos = Addrinfo.getaddrinfo(host, port, ADDRESS_FAMILIES[:family_name], :STREAM, timeout: open_timeout || resolv_timeout) + resolution_store.add_resolved(family_name, addrinfos) + hostname_resolution_result = nil + hostname_resolution_notifier = nil + user_specified_resolv_timeout_at = nil + else + hostname_resolution_result = HostnameResolutionResult.new(resolving_family_names.size) + hostname_resolution_notifier = hostname_resolution_result.notifier + + hostname_resolution_threads.concat( + resolving_family_names.map { |family| + thread_args = [family, host, port, hostname_resolution_result] + thread = Thread.new(*thread_args) { |*thread_args| resolve_hostname(*thread_args) } + Thread.pass + thread + } + ) + user_specified_resolv_timeout_at = resolv_timeout ? now + resolv_timeout : Float::INFINITY + end + + loop do + if resolution_store.any_addrinfos? && + !resolution_delay_expires_at && + !connection_attempt_delay_expires_at + while (addrinfo = resolution_store.get_addrinfo) + if local_addrinfos.any? + local_addrinfo = local_addrinfos.find { |lai| lai.afamily == addrinfo.afamily } + + if local_addrinfo.nil? + if resolution_store.any_addrinfos? + # Try other Addrinfo in next "while" + next + elsif connecting_sockets.any? || resolution_store.any_unresolved_family? + # Exit this "while" and wait for connections to be established or hostname resolution in next loop + # Or exit this "while" and wait for hostname resolution in next loop + break + else + raise SocketError.new 'no appropriate local address' + end + end + end + + begin + if resolution_store.any_addrinfos? || + connecting_sockets.any? || + resolution_store.any_unresolved_family? + socket = Socket.new(addrinfo.pfamily, addrinfo.socktype, addrinfo.protocol) + socket.bind(local_addrinfo) if local_addrinfo + result = socket.connect_nonblock(addrinfo, exception: false) + else + timeout = + if open_timeout + t = open_timeout - (current_clock_time - starts_at) + t.negative? ? 0 : t + else + connect_timeout + end + result = socket = local_addrinfo ? + addrinfo.connect_from(local_addrinfo, timeout:) : + addrinfo.connect(timeout:) + end + + if result == :wait_writable + connection_attempt_delay_expires_at = now + CONNECTION_ATTEMPT_DELAY + if resolution_store.empty_addrinfos? + user_specified_connect_timeout_at = connect_timeout ? now + connect_timeout : Float::INFINITY + end + + connecting_sockets[socket] = addrinfo + break + else + return socket # connection established + end + rescue SystemCallError => e + socket&.close + last_error = e + + if resolution_store.any_addrinfos? + # Try other Addrinfo in next "while" + next + elsif connecting_sockets.any? || resolution_store.any_unresolved_family? + # Exit this "while" and wait for connections to be established or hostname resolution in next loop + # Or exit this "while" and wait for hostname resolution in next loop + break + else + raise last_error + end + end + end + end + + ends_at = + if resolution_store.any_addrinfos? + [(resolution_delay_expires_at || connection_attempt_delay_expires_at), + user_specified_open_timeout_at].compact.min + elsif user_specified_open_timeout_at + user_specified_open_timeout_at + else + [user_specified_resolv_timeout_at, user_specified_connect_timeout_at].compact.max + end + + hostname_resolved, writable_sockets, except_sockets = IO.select( + hostname_resolution_notifier, + connecting_sockets.keys, + # Use errorfds to wait for non-blocking connect failures on Windows + is_windows_environment ? connecting_sockets.keys : nil, + second_to_timeout(current_clock_time, ends_at), + ) + now = current_clock_time + resolution_delay_expires_at = nil if expired?(now, resolution_delay_expires_at) + connection_attempt_delay_expires_at = nil if expired?(now, connection_attempt_delay_expires_at) + + if writable_sockets&.any? + while (writable_socket = writable_sockets.pop) + is_connected = is_windows_environment || ( + sockopt = writable_socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR) + sockopt.int.zero? + ) + + if is_connected + connecting_sockets.delete writable_socket + return writable_socket + else + failed_ai = connecting_sockets.delete writable_socket + writable_socket.close + ip_address = failed_ai.ipv6? ? "[#{failed_ai.ip_address}]" : failed_ai.ip_address + last_error = SystemCallError.new("connect(2) for #{ip_address}:#{failed_ai.ip_port}", sockopt.int) + + if writable_sockets.any? || connecting_sockets.any? + # Try other writable socket in next "while" + # Or exit this "while" and wait for connections to be established or hostname resolution in next loop + elsif resolution_store.any_addrinfos? || resolution_store.any_unresolved_family? + # Exit this "while" and try other connection attempt + # Or exit this "while" and wait for hostname resolution in next loop + connection_attempt_delay_expires_at = nil + user_specified_connect_timeout_at = nil + else + raise last_error + end + end + end + end + + if except_sockets&.any? + except_sockets.each do |except_socket| + failed_ai = connecting_sockets.delete except_socket + sockopt = except_socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR) + except_socket.close + ip_address = failed_ai.ipv6? ? "[#{failed_ai.ip_address}]" : failed_ai.ip_address + last_error = SystemCallError.new("connect(2) for #{ip_address}:#{failed_ai.ip_port}", sockopt.int) + + if except_sockets.any? || connecting_sockets.any? + # Cleanup other except socket in next "each" + # Or exit this "while" and wait for connections to be established or hostname resolution in next loop + elsif resolution_store.any_addrinfos? || resolution_store.any_unresolved_family? + # Exit this "while" and try other connection attempt + # Or exit this "while" and wait for hostname resolution in next loop + connection_attempt_delay_expires_at = nil + user_specified_connect_timeout_at = nil + else + raise last_error + end + end + end + + if hostname_resolved&.any? + while (family_and_result = hostname_resolution_result.get) + family_name, result = family_and_result + + if result.is_a? Exception + resolution_store.add_error(family_name, result) + + unless (Socket.const_defined?(:EAI_ADDRFAMILY)) && + (result.is_a?(Socket::ResolutionError)) && + (result.error_code == Socket::EAI_ADDRFAMILY) + other = family_name == :ipv6 ? :ipv4 : :ipv6 + if !resolution_store.resolved?(other) || !resolution_store.resolved_successfully?(other) + last_error = result + last_error_from_thread = true + end + end + else + resolution_store.add_resolved(family_name, result) + end + end + + if resolution_store.resolved?(:ipv4) + if resolution_store.resolved?(:ipv6) + hostname_resolution_notifier = nil + resolution_delay_expires_at = nil + user_specified_resolv_timeout_at = nil + elsif resolution_store.resolved_successfully?(:ipv4) + resolution_delay_expires_at = now + RESOLUTION_DELAY + end + end + end + + if expired?(now, user_specified_open_timeout_at) + raise(IO::TimeoutError, "user specified timeout for #{host}:#{port}") + end + + if resolution_store.empty_addrinfos? + if connecting_sockets.empty? && resolution_store.resolved_all_families? + if last_error_from_thread + raise last_error.class, last_error.message, cause: last_error + else + raise last_error + end + end + + if (expired?(now, user_specified_resolv_timeout_at) || resolution_store.resolved_all_families?) && + (expired?(now, user_specified_connect_timeout_at) || connecting_sockets.empty?) + raise(IO::TimeoutError, "user specified timeout for #{host}:#{port}") + end + end + end + ensure + hostname_resolution_threads.each do |thread| + thread.exit + end + + hostname_resolution_result&.close + + connecting_sockets.each_key do |connecting_socket| + connecting_socket.close + end + end + private_class_method :tcp_with_fast_fallback + + def self.tcp_without_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:, open_timeout:) last_error = nil ret = nil local_addr_list = nil if local_host != nil || local_port != nil - local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil) + local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil, timeout: open_timeout || resolv_timeout) end - Addrinfo.foreach(host, port, nil, :STREAM, timeout: resolv_timeout) {|ai| + timeout = open_timeout ? open_timeout : resolv_timeout + starts_at = current_clock_time + + Addrinfo.foreach(host, port, nil, :STREAM, timeout:) {|ai| if local_addr_list local_addr = local_addr_list.find {|local_ai| local_ai.afamily == ai.afamily } next unless local_addr @@ -642,9 +956,17 @@ class Socket < BasicSocket local_addr = nil end begin + timeout = + if open_timeout + t = open_timeout - (current_clock_time - starts_at) + t.negative? ? 0 : t + else + connect_timeout + end + sock = local_addr ? - ai.connect_from(local_addr, timeout: connect_timeout) : - ai.connect(timeout: connect_timeout) + ai.connect_from(local_addr, timeout:) : + ai.connect(timeout:) rescue SystemCallError last_error = $! next @@ -659,18 +981,150 @@ class Socket < BasicSocket raise SocketError, "no appropriate local address" end end - if block_given? - begin - yield ret - ensure - ret.close + + ret + end + private_class_method :tcp_without_fast_fallback + + def self.ip_address?(hostname) + hostname.match?(IPV6_ADDRESS_FORMAT) || hostname.match?(/\A([0-9]{1,3}\.){3}[0-9]{1,3}\z/) + end + private_class_method :ip_address? + + def self.resolve_hostname(family, host, port, hostname_resolution_result) + begin + resolved_addrinfos = Addrinfo.getaddrinfo(host, port, ADDRESS_FAMILIES[family], :STREAM) + hostname_resolution_result.add(family, resolved_addrinfos) + rescue => e + hostname_resolution_result.add(family, e) + end + end + private_class_method :resolve_hostname + + def self.current_clock_time + Process.clock_gettime(Process::CLOCK_MONOTONIC) + end + private_class_method :current_clock_time + + def self.second_to_timeout(started_at, ends_at) + return nil if ends_at == Float::INFINITY || ends_at.nil? + + remaining = (ends_at - started_at) + remaining.negative? ? 0 : remaining + end + private_class_method :second_to_timeout + + def self.expired?(started_at, ends_at) + second_to_timeout(started_at, ends_at)&.zero? + end + private_class_method :expired? + + class HostnameResolutionResult + def initialize(size) + @size = size + @taken_count = 0 + @rpipe, @wpipe = IO.pipe + @results = [] + @mutex = Mutex.new + end + + def notifier + [@rpipe] + end + + def add(family, result) + @mutex.synchronize do + @results.push [family, result] + @wpipe.putc HOSTNAME_RESOLUTION_QUEUE_UPDATED end - else - ret + end + + def get + return nil if @results.empty? + + res = nil + + @mutex.synchronize do + @rpipe.getbyte + res = @results.shift + end + + @taken_count += 1 + close if @taken_count == @size + res + end + + def close + @rpipe.close + @wpipe.close end end + private_constant :HostnameResolutionResult + + class HostnameResolutionStore + PRIORITY_ON_V6 = [:ipv6, :ipv4].freeze + PRIORITY_ON_V4 = [:ipv4, :ipv6].freeze + + def initialize(family_names) + @family_names = family_names + @addrinfo_dict = {} + @error_dict = {} + @last_family = nil + end + + def add_resolved(family_name, addrinfos) + @addrinfo_dict[family_name] = addrinfos + end + + def add_error(family_name, error) + @addrinfo_dict[family_name] = [] + @error_dict[family_name] = error + end + + def get_addrinfo + precedences = + case @last_family + when :ipv4, nil then PRIORITY_ON_V6 + when :ipv6 then PRIORITY_ON_V4 + end + + precedences.each do |family_name| + addrinfo = @addrinfo_dict[family_name]&.shift + next unless addrinfo + + @last_family = family_name + return addrinfo + end + + nil + end + + def empty_addrinfos? + @addrinfo_dict.all? { |_, addrinfos| addrinfos.empty? } + end + + def any_addrinfos? + !empty_addrinfos? + end + + def resolved?(family) + @addrinfo_dict.has_key? family + end + + def resolved_successfully?(family) + resolved?(family) && !@error_dict[family] + end + + def resolved_all_families? + (@family_names - @addrinfo_dict.keys).empty? + end + + def any_unresolved_family? + !resolved_all_families? + end + end + private_constant :HostnameResolutionStore - # :stopdoc: def self.ip_sockets_port0(ai_list, reuseaddr) sockets = [] begin @@ -703,9 +1157,7 @@ class Socket < BasicSocket end sockets end - class << self - private :ip_sockets_port0 - end + private_class_method :ip_sockets_port0 def self.tcp_server_sockets_port0(host) ai_list = Addrinfo.getaddrinfo(host, 0, nil, :STREAM, nil, Socket::AI_PASSIVE) @@ -1133,13 +1585,18 @@ class Socket < BasicSocket end end - class << self - private - - def unix_socket_abstract_name?(path) - /linux/ =~ RUBY_PLATFORM && /\A(\0|\z)/ =~ path + # :stopdoc: + if RUBY_PLATFORM.include?("linux") + def self.unix_socket_abstract_name?(path) + path.empty? or path.start_with?("\0") + end + else + def self.unix_socket_abstract_name?(path) + false end end + private_class_method :unix_socket_abstract_name? + # :startdoc: # creates a UNIX socket server on _path_. # It calls the block for each socket accepted. @@ -1179,7 +1636,7 @@ class Socket < BasicSocket # Returns 0 if successful, otherwise an exception is raised. # # === Parameter - # # +remote_sockaddr+ - the +struct+ sockaddr contained in a string or Addrinfo object + # * +remote_sockaddr+ - the +struct+ sockaddr contained in a string or Addrinfo object # # === Example: # # Pull down Google's web page @@ -1214,7 +1671,7 @@ class Socket < BasicSocket # return the symbol +:wait_writable+ instead. # # === See - # # Socket#connect + # * Socket#connect def connect_nonblock(addr, exception: true) __connect_nonblock(addr, exception) end diff --git a/ext/socket/mkconstants.rb b/ext/socket/mkconstants.rb index ba7a55b426..4271e40cd8 100644 --- a/ext/socket/mkconstants.rb +++ b/ext/socket/mkconstants.rb @@ -76,15 +76,11 @@ def each_name(pat) } end -erb_new = lambda do |src, safe, trim| - if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+ - ERB.new(src, trim_mode: trim) - else - ERB.new(src, safe, trim) - end +erb_new = lambda do |src, trim| + ERB.new(src, trim_mode: trim) end -erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_decls") +erb_new.call(<<'EOS', '%').def_method(Object, "gen_const_decls") % each_const {|guard, name, default_value| #if !defined(<%=name%>) # if defined(HAVE_CONST_<%=name.upcase%>) @@ -98,7 +94,7 @@ erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_decls") % } EOS -erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_defs_in_guard(name, default_value)") +erb_new.call(<<'EOS', '%').def_method(Object, "gen_const_defs_in_guard(name, default_value)") #if defined(<%=name%>) /* <%= COMMENTS[name] %> */ rb_define_const(rb_cSocket, <%=c_str name%>, INTEGER2NUM(<%=name%>)); @@ -107,7 +103,7 @@ erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_defs_in_guard(name #endif EOS -erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_defs") +erb_new.call(<<'EOS', '%').def_method(Object, "gen_const_defs") % each_const {|guard, name, default_value| % if guard #if <%=guard%> @@ -157,7 +153,7 @@ def each_names_with_len(pat, prefix_optional=nil) } end -erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_name_to_int_decl(funcname, pat, prefix_optional, guard=nil)") +erb_new.call(<<'EOS', '%').def_method(Object, "gen_name_to_int_decl(funcname, pat, prefix_optional, guard=nil)") %if guard #ifdef <%=guard%> int <%=funcname%>(const char *str, long len, int *valp); @@ -167,7 +163,7 @@ int <%=funcname%>(const char *str, long len, int *valp); %end EOS -erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_name_to_int_func_in_guard(funcname, pat, prefix_optional, guard=nil)") +erb_new.call(<<'EOS', '%').def_method(Object, "gen_name_to_int_func_in_guard(funcname, pat, prefix_optional, guard=nil)") int <%=funcname%>(const char *str, long len, int *valp) { @@ -189,7 +185,7 @@ int } EOS -erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_name_to_int_func(funcname, pat, prefix_optional, guard=nil)") +erb_new.call(<<'EOS', '%').def_method(Object, "gen_name_to_int_func(funcname, pat, prefix_optional, guard=nil)") %if guard #ifdef <%=guard%> <%=gen_name_to_int_func_in_guard(funcname, pat, prefix_optional, guard)%> @@ -218,7 +214,7 @@ def reverse_each_name_with_prefix_optional(pat, prefix_pat) end end -erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_int_to_name_hash(hash_var, pat, prefix_pat)") +erb_new.call(<<'EOS', '%').def_method(Object, "gen_int_to_name_hash(hash_var, pat, prefix_pat)") <%=hash_var%> = st_init_numtable(); % reverse_each_name_with_prefix_optional(pat, prefix_pat) {|n,s| #ifdef <%=n%> @@ -227,7 +223,7 @@ erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_int_to_name_hash(hash_va % } EOS -erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_int_to_name_func(func_name, hash_var)") +erb_new.call(<<'EOS', '%').def_method(Object, "gen_int_to_name_func(func_name, hash_var)") ID <%=func_name%>(int val) { @@ -238,7 +234,7 @@ ID } EOS -erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_int_to_name_decl(func_name, hash_var)") +erb_new.call(<<'EOS', '%').def_method(Object, "gen_int_to_name_decl(func_name, hash_var)") ID <%=func_name%>(int val); EOS @@ -287,7 +283,7 @@ def_intern('rsock_intern_udp_optname', /\AUDP_/, "UDP_") def_intern('rsock_intern_scm_optname', /\ASCM_/, "SCM_") def_intern('rsock_intern_local_optname', /\ALOCAL_/, "LOCAL_") -result = erb_new.call(<<'EOS', nil, '%').result(binding) +result = erb_new.call(<<'EOS', '%').result(binding) /* autogenerated file */ <%= INTERN_DEFS.map {|vardef, gen_hash, decl, func| vardef }.join("\n") %> @@ -330,7 +326,7 @@ init_constants(void) EOS -header_result = erb_new.call(<<'EOS', nil, '%').result(binding) +header_result = erb_new.call(<<'EOS', '%').result(binding) /* autogenerated file */ <%= gen_const_decls %> <%= NAME_TO_INT_DEFS.map {|decl, func| decl }.join("\n") %> @@ -669,6 +665,7 @@ SO_SETFIB nil Set the associated routing table for the socket (FreeBSD SO_RTABLE nil Set the routing table for this socket (OpenBSD) SO_INCOMING_CPU nil Receive the cpu attached to the socket (Linux 3.19) SO_INCOMING_NAPI_ID nil Receive the napi ID attached to a RX queue (Linux 4.12) +SO_CONNECT_TIME nil Returns the number of seconds a socket has been connected. This option is only valid for connection-oriented protocols (Windows) SOPRI_INTERACTIVE nil Interactive socket priority SOPRI_NORMAL nil Normal socket priority @@ -748,6 +745,7 @@ SHUT_RDWR 2 Shut down the both sides of the socket IPV6_JOIN_GROUP nil Join a group membership IPV6_LEAVE_GROUP nil Leave a group membership +IPV6_MTU_DISCOVER nil Path MTU discovery IPV6_MULTICAST_HOPS nil IP6 multicast hops IPV6_MULTICAST_IF nil IP6 multicast interface IPV6_MULTICAST_LOOP nil IP6 multicast loopback @@ -762,6 +760,7 @@ IPV6_NEXTHOP nil Next hop address IPV6_PATHMTU nil Retrieve current path MTU IPV6_PKTINFO nil Receive packet information with datagram IPV6_RECVDSTOPTS nil Receive all IP6 options for response +IPV6_RECVERR nil Enable extended reliable error message passing IPV6_RECVHOPLIMIT nil Receive hop limit with datagram IPV6_RECVHOPOPTS nil Receive hop-by-hop options IPV6_RECVPKTINFO nil Receive destination IP address and incoming interface diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c index e79bcfa332..6cdf5c6abc 100644 --- a/ext/socket/raddrinfo.c +++ b/ext/socket/raddrinfo.c @@ -277,8 +277,9 @@ numeric_getaddrinfo(const char *node, const char *service, void rb_freeaddrinfo(struct rb_addrinfo *ai) { - if (!ai->allocated_by_malloc) - freeaddrinfo(ai->ai); + if (!ai->allocated_by_malloc) { + if (ai->ai) freeaddrinfo(ai->ai); + } else { struct addrinfo *ai1, *ai2; ai1 = ai->ai; @@ -292,10 +293,22 @@ rb_freeaddrinfo(struct rb_addrinfo *ai) xfree(ai); } +static int +rsock_value_timeout_to_msec(VALUE timeout) +{ + double seconds = NUM2DBL(timeout); + if (seconds < 0) rb_raise(rb_eArgError, "timeout must not be negative"); + + double msec = seconds * 1000.0; + if (msec > UINT_MAX) rb_raise(rb_eArgError, "timeout too large"); + + return (unsigned int)(msec + 0.5); +} + #if GETADDRINFO_IMPL == 0 static int -rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai) +rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai, int _timeout) { return getaddrinfo(hostp, portp, hints, ai); } @@ -326,8 +339,14 @@ nogvl_getaddrinfo(void *arg) return (void *)(VALUE)ret; } +static void * +fork_safe_getaddrinfo(void *arg) +{ + return rb_thread_prevent_fork(nogvl_getaddrinfo, arg); +} + static int -rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai) +rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai, int _timeout) { struct getaddrinfo_arg arg; MEMZERO(&arg, struct getaddrinfo_arg, 1); @@ -335,7 +354,7 @@ rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hint arg.service = portp; arg.hints = hints; arg.res = ai; - return (int)(VALUE)rb_thread_call_without_gvl(nogvl_getaddrinfo, &arg, RUBY_UBF_IO, 0); + return (int)(VALUE)rb_thread_call_without_gvl(fork_safe_getaddrinfo, &arg, RUBY_UBF_IO, 0); } #elif GETADDRINFO_IMPL == 2 @@ -345,13 +364,14 @@ struct getaddrinfo_arg char *node, *service; struct addrinfo hints; struct addrinfo *ai; - int err, gai_errno, refcount, done, cancelled; + int err, gai_errno, refcount, done, cancelled, timedout; rb_nativethread_lock_t lock; rb_nativethread_cond_t cond; + int timeout; }; static struct getaddrinfo_arg * -allocate_getaddrinfo_arg(const char *hostp, const char *portp, const struct addrinfo *hints) +allocate_getaddrinfo_arg(const char *hostp, const char *portp, const struct addrinfo *hints, int timeout) { size_t hostp_offset = sizeof(struct getaddrinfo_arg); size_t portp_offset = hostp_offset + (hostp ? strlen(hostp) + 1 : 0); @@ -367,7 +387,7 @@ allocate_getaddrinfo_arg(const char *hostp, const char *portp, const struct addr if (hostp) { arg->node = buf + hostp_offset; - strcpy(arg->node, hostp); + memcpy(arg->node, hostp, portp_offset - hostp_offset); } else { arg->node = NULL; @@ -375,7 +395,7 @@ allocate_getaddrinfo_arg(const char *hostp, const char *portp, const struct addr if (portp) { arg->service = buf + portp_offset; - strcpy(arg->service, portp); + memcpy(arg->service, portp, bufsize - portp_offset); } else { arg->service = NULL; @@ -385,7 +405,8 @@ allocate_getaddrinfo_arg(const char *hostp, const char *portp, const struct addr arg->ai = NULL; arg->refcount = 2; - arg->done = arg->cancelled = 0; + arg->done = arg->cancelled = arg->timedout = 0; + arg->timeout = timeout; rb_nativethread_lock_initialize(&arg->lock); rb_native_cond_initialize(&arg->cond); @@ -423,7 +444,7 @@ do_getaddrinfo(void *ptr) arg->err = err; arg->gai_errno = gai_errno; if (arg->cancelled) { - freeaddrinfo(arg->ai); + if (arg->ai) freeaddrinfo(arg->ai); } else { arg->done = 1; @@ -444,7 +465,19 @@ wait_getaddrinfo(void *ptr) struct getaddrinfo_arg *arg = (struct getaddrinfo_arg *)ptr; rb_nativethread_lock_lock(&arg->lock); while (!arg->done && !arg->cancelled) { - rb_native_cond_wait(&arg->cond, &arg->lock); + long msec = arg->timeout; + if (msec == 0) { + arg->cancelled = 1; + arg->timedout = 1; + } else if (msec > 0) { + rb_native_cond_timedwait(&arg->cond, &arg->lock, msec); + if (!arg->done) { + arg->cancelled = 1; + arg->timedout = 1; + } + } else { + rb_native_cond_wait(&arg->cond, &arg->lock); + } } rb_nativethread_lock_unlock(&arg->lock); return 0; @@ -462,41 +495,84 @@ cancel_getaddrinfo(void *ptr) rb_nativethread_lock_unlock(&arg->lock); } -static int -do_pthread_create(pthread_t *th, void *(*start_routine) (void *), void *arg) +int +raddrinfo_pthread_create(pthread_t *th, void *(*start_routine) (void *), void *arg) { int limit = 3, ret; + int saved_errno; +#ifdef HAVE_PTHREAD_ATTR_SETDETACHSTATE + pthread_attr_t attr; + pthread_attr_t *attr_p = &attr; + int err; + int init_retries = 0; + int init_retries_max = 3; +retry_attr_init: + if ((err = pthread_attr_init(attr_p)) != 0) { + if (err == ENOMEM && init_retries < init_retries_max) { + init_retries++; + rb_gc(); + goto retry_attr_init; + } + return err; + } + if ((err = pthread_attr_setdetachstate(attr_p, PTHREAD_CREATE_DETACHED)) != 0) { + saved_errno = errno; + pthread_attr_destroy(attr_p); + errno = saved_errno; + return err; // EINVAL - shouldn't happen + } +#else + pthread_attr_t *attr_p = NULL; +#endif do { // It is said that pthread_create may fail spuriously, so we follow the JDK and retry several times. // // https://bugs.openjdk.org/browse/JDK-8268605 // https://github.com/openjdk/jdk/commit/e35005d5ce383ddd108096a3079b17cb0bcf76f1 - ret = pthread_create(th, 0, start_routine, arg); + ret = pthread_create(th, attr_p, start_routine, arg); } while (ret == EAGAIN && limit-- > 0); +#ifdef HAVE_PTHREAD_ATTR_SETDETACHSTATE + saved_errno = errno; + pthread_attr_destroy(attr_p); + if (ret != 0) { + errno = saved_errno; + } +#else + if (ret == 0) { + pthread_detach(th); // this can race with shutdown routine of thread in some glibc versions + } +#endif return ret; } +static void * +fork_safe_do_getaddrinfo(void *ptr) +{ + return rb_thread_prevent_fork(do_getaddrinfo, ptr); +} + static int -rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai) +rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai, int timeout) { int retry; struct getaddrinfo_arg *arg; - int err, gai_errno = 0; + int err = 0, gai_errno = 0, timedout = 0; start: retry = 0; - arg = allocate_getaddrinfo_arg(hostp, portp, hints); + arg = allocate_getaddrinfo_arg(hostp, portp, hints, timeout); if (!arg) { return EAI_MEMORY; } pthread_t th; - if (do_pthread_create(&th, do_getaddrinfo, arg) != 0) { + if (raddrinfo_pthread_create(&th, fork_safe_do_getaddrinfo, arg) != 0) { + int err = errno; free_getaddrinfo_arg(arg); - return EAI_AGAIN; + errno = err; + return EAI_SYSTEM; } - pthread_detach(th); rb_thread_call_without_gvl2(wait_getaddrinfo, arg, cancel_getaddrinfo, arg); @@ -509,7 +585,8 @@ start: if (err == 0) *ai = arg->ai; } else if (arg->cancelled) { - err = EAI_AGAIN; + retry = 1; + timedout = arg->timedout; } else { // If already interrupted, rb_thread_call_without_gvl2 may return without calling wait_getaddrinfo. @@ -523,6 +600,16 @@ start: if (need_free) free_getaddrinfo_arg(arg); + if (timedout) { + if (arg->ai) { + rsock_raise_user_specified_timeout(arg->ai, Qnil, Qnil); + } else { + VALUE host = rb_str_new_cstr(hostp); + VALUE port = rb_str_new_cstr(portp); + rsock_raise_user_specified_timeout(NULL, host, port); + } + } + // If the current thread is interrupted by asynchronous exception, the following raises the exception. // But if the current thread is interrupted by timer thread, the following returns; we need to manually retry. rb_thread_check_ints(); @@ -537,6 +624,10 @@ start: #endif +#define GETNAMEINFO_WONT_BLOCK(host, serv, flags) \ + ((!(host) || ((flags) & NI_NUMERICHOST)) && \ + (!(serv) || ((flags) & NI_NUMERICSERV))) + #if GETADDRINFO_IMPL == 0 int @@ -544,7 +635,7 @@ rb_getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags) { - return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); + return getnameinfo(sa, salen, host, (socklen_t)hostlen, serv, (socklen_t)servlen, flags); } #elif GETADDRINFO_IMPL == 1 @@ -574,6 +665,10 @@ rb_getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags) { + if (GETNAMEINFO_WONT_BLOCK(host, serv, flags)) { + return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); + } + struct getnameinfo_arg arg; int ret; arg.sa = sa; @@ -700,7 +795,11 @@ rb_getnameinfo(const struct sockaddr *sa, socklen_t salen, { int retry; struct getnameinfo_arg *arg; - int err, gni_errno = 0; + int err = 0, gni_errno = 0; + + if (GETNAMEINFO_WONT_BLOCK(host, serv, flags)) { + return getnameinfo(sa, salen, host, (socklen_t)hostlen, serv, (socklen_t)servlen, flags); + } start: retry = 0; @@ -711,11 +810,12 @@ start: } pthread_t th; - if (do_pthread_create(&th, do_getnameinfo, arg) != 0) { + if (raddrinfo_pthread_create(&th, do_getnameinfo, arg) != 0) { + int err = errno; free_getnameinfo_arg(arg); - return EAI_AGAIN; + errno = err; + return EAI_SYSTEM; } - pthread_detach(th); rb_thread_call_without_gvl2(wait_getnameinfo, arg, cancel_getnameinfo, arg); @@ -730,7 +830,7 @@ start: } } else if (arg->cancelled) { - err = EAI_AGAIN; + retry = 1; } else { // If already interrupted, rb_thread_call_without_gvl2 may return without calling wait_getnameinfo. @@ -805,8 +905,8 @@ str_is_number(const char *p) ((ptr)[0] == name[0] && \ rb_strlen_lit(name) == (len) && memcmp(ptr, name, len) == 0) -static char* -host_str(VALUE host, char *hbuf, size_t hbuflen, int *flags_ptr) +char* +raddrinfo_host_str(VALUE host, char *hbuf, size_t hbuflen, int *flags_ptr) { if (NIL_P(host)) { return NULL; @@ -844,8 +944,8 @@ host_str(VALUE host, char *hbuf, size_t hbuflen, int *flags_ptr) } } -static char* -port_str(VALUE port, char *pbuf, size_t pbuflen, int *flags_ptr) +char* +raddrinfo_port_str(VALUE port, char *pbuf, size_t pbuflen, int *flags_ptr) { if (NIL_P(port)) { return 0; @@ -897,7 +997,7 @@ rb_scheduler_getaddrinfo(VALUE scheduler, VALUE host, const char *service, for(i=0; i<len; i++) { ip_address = rb_ary_entry(ip_addresses_array, i); - hostp = host_str(ip_address, _hbuf, sizeof(_hbuf), &_additional_flags); + hostp = raddrinfo_host_str(ip_address, _hbuf, sizeof(_hbuf), &_additional_flags); error = numeric_getaddrinfo(hostp, service, hints, &ai); if (error == 0) { if (!res_allocated) { @@ -924,7 +1024,7 @@ rb_scheduler_getaddrinfo(VALUE scheduler, VALUE host, const char *service, } struct rb_addrinfo* -rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_hack) +rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_hack, VALUE timeout) { struct rb_addrinfo* res = NULL; struct addrinfo *ai; @@ -933,8 +1033,8 @@ rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_h char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; int additional_flags = 0; - hostp = host_str(host, hbuf, sizeof(hbuf), &additional_flags); - portp = port_str(port, pbuf, sizeof(pbuf), &additional_flags); + hostp = raddrinfo_host_str(host, hbuf, sizeof(hbuf), &additional_flags); + portp = raddrinfo_port_str(port, pbuf, sizeof(pbuf), &additional_flags); if (socktype_hack && hints->ai_socktype == 0 && str_is_number(portp)) { hints->ai_socktype = SOCK_DGRAM; @@ -959,7 +1059,8 @@ rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_h } if (!resolved) { - error = rb_getaddrinfo(hostp, portp, hints, &ai); + int t = NIL_P(timeout) ? -1 : rsock_value_timeout_to_msec(timeout); + error = rb_getaddrinfo(hostp, portp, hints, &ai, t); if (error == 0) { res = (struct rb_addrinfo *)xmalloc(sizeof(struct rb_addrinfo)); res->allocated_by_malloc = 0; @@ -992,7 +1093,7 @@ rsock_fd_family(int fd) } struct rb_addrinfo* -rsock_addrinfo(VALUE host, VALUE port, int family, int socktype, int flags) +rsock_addrinfo(VALUE host, VALUE port, int family, int socktype, int flags, VALUE timeout) { struct addrinfo hints; @@ -1000,7 +1101,7 @@ rsock_addrinfo(VALUE host, VALUE port, int family, int socktype, int flags) hints.ai_family = family; hints.ai_socktype = socktype; hints.ai_flags = flags; - return rsock_getaddrinfo(host, port, &hints, 1); + return rsock_getaddrinfo(host, port, &hints, 1, timeout); } VALUE @@ -1120,7 +1221,7 @@ make_hostent_internal(VALUE v) hostp = addr->ai_canonname; } else { - hostp = host_str(host, hbuf, sizeof(hbuf), NULL); + hostp = raddrinfo_host_str(host, hbuf, sizeof(hbuf), NULL); } rb_ary_push(ary, rb_str_new2(hostp)); @@ -1194,6 +1295,7 @@ addrinfo_memsize(const void *ptr) static const rb_data_type_t addrinfo_type = { "socket/addrinfo", {addrinfo_mark, addrinfo_free, addrinfo_memsize,}, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED, }; static VALUE @@ -1231,7 +1333,7 @@ alloc_addrinfo(void) } static void -init_addrinfo(rb_addrinfo_t *rai, struct sockaddr *sa, socklen_t len, +init_addrinfo(VALUE self, rb_addrinfo_t *rai, struct sockaddr *sa, socklen_t len, int pfamily, int socktype, int protocol, VALUE canonname, VALUE inspectname) { @@ -1243,8 +1345,8 @@ init_addrinfo(rb_addrinfo_t *rai, struct sockaddr *sa, socklen_t len, rai->pfamily = pfamily; rai->socktype = socktype; rai->protocol = protocol; - rai->canonname = canonname; - rai->inspectname = inspectname; + RB_OBJ_WRITE(self, &rai->canonname, canonname); + RB_OBJ_WRITE(self, &rai->inspectname, inspectname); } VALUE @@ -1257,7 +1359,7 @@ rsock_addrinfo_new(struct sockaddr *addr, socklen_t len, a = addrinfo_s_allocate(rb_cAddrinfo); DATA_PTR(a) = rai = alloc_addrinfo(); - init_addrinfo(rai, addr, len, family, socktype, protocol, canonname, inspectname); + init_addrinfo(a, rai, addr, len, family, socktype, protocol, canonname, inspectname); return a; } @@ -1282,7 +1384,7 @@ call_getaddrinfo(VALUE node, VALUE service, hints.ai_flags = NUM2INT(flags); } - res = rsock_getaddrinfo(node, service, &hints, socktype_hack); + res = rsock_getaddrinfo(node, service, &hints, socktype_hack, timeout); if (res == NULL) rb_raise(rb_eSocket, "host not found"); @@ -1292,7 +1394,7 @@ call_getaddrinfo(VALUE node, VALUE service, static VALUE make_inspectname(VALUE node, VALUE service, struct addrinfo *res); static void -init_addrinfo_getaddrinfo(rb_addrinfo_t *rai, VALUE node, VALUE service, +init_addrinfo_getaddrinfo(VALUE self, rb_addrinfo_t *rai, VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE protocol, VALUE flags, VALUE inspectnode, VALUE inspectservice) { @@ -1306,7 +1408,7 @@ init_addrinfo_getaddrinfo(rb_addrinfo_t *rai, VALUE node, VALUE service, OBJ_FREEZE(canonname); } - init_addrinfo(rai, res->ai->ai_addr, res->ai->ai_addrlen, + init_addrinfo(self, rai, res->ai->ai_addr, res->ai->ai_addrlen, NUM2INT(family), NUM2INT(socktype), NUM2INT(protocol), canonname, inspectname); @@ -1418,7 +1520,7 @@ addrinfo_list_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE #ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN static void -init_unix_addrinfo(rb_addrinfo_t *rai, VALUE path, int socktype) +init_unix_addrinfo(VALUE self, rb_addrinfo_t *rai, VALUE path, int socktype) { struct sockaddr_un un; socklen_t len; @@ -1434,7 +1536,7 @@ init_unix_addrinfo(rb_addrinfo_t *rai, VALUE path, int socktype) memcpy((void*)&un.sun_path, RSTRING_PTR(path), RSTRING_LEN(path)); len = rsock_unix_sockaddr_len(path); - init_addrinfo(rai, (struct sockaddr *)&un, len, + init_addrinfo(self, rai, (struct sockaddr *)&un, len, PF_UNIX, socktype, 0, Qnil, Qnil); } @@ -1538,7 +1640,7 @@ addrinfo_initialize(int argc, VALUE *argv, VALUE self) flags |= AI_NUMERICSERV; #endif - init_addrinfo_getaddrinfo(rai, numericnode, service, + init_addrinfo_getaddrinfo(self, rai, numericnode, service, INT2NUM(i_pfamily ? i_pfamily : af), INT2NUM(i_socktype), INT2NUM(i_protocol), INT2NUM(flags), nodename, service); @@ -1550,7 +1652,7 @@ addrinfo_initialize(int argc, VALUE *argv, VALUE self) { VALUE path = rb_ary_entry(sockaddr_ary, 1); StringValue(path); - init_unix_addrinfo(rai, path, SOCK_STREAM); + init_unix_addrinfo(self, rai, path, SOCK_STREAM); break; } #endif @@ -1563,7 +1665,7 @@ addrinfo_initialize(int argc, VALUE *argv, VALUE self) StringValue(sockaddr_arg); sockaddr_ptr = (struct sockaddr *)RSTRING_PTR(sockaddr_arg); sockaddr_len = RSTRING_SOCKLEN(sockaddr_arg); - init_addrinfo(rai, sockaddr_ptr, sockaddr_len, + init_addrinfo(self, rai, sockaddr_ptr, sockaddr_len, i_pfamily, i_socktype, i_protocol, canonname, inspectname); } @@ -2152,7 +2254,7 @@ addrinfo_mload(VALUE self, VALUE ary) } DATA_PTR(self) = rai = alloc_addrinfo(); - init_addrinfo(rai, &ss.addr, len, + init_addrinfo(self, rai, &ss.addr, len, pfamily, socktype, protocol, canonname, inspectname); return self; @@ -2920,7 +3022,7 @@ addrinfo_s_unix(int argc, VALUE *argv, VALUE self) addr = addrinfo_s_allocate(rb_cAddrinfo); DATA_PTR(addr) = rai = alloc_addrinfo(); - init_unix_addrinfo(rai, path, socktype); + init_unix_addrinfo(self, rai, path, socktype); return addr; } @@ -3007,6 +3109,99 @@ rsock_io_socket_addrinfo(VALUE io, struct sockaddr *addr, socklen_t len) UNREACHABLE_RETURN(Qnil); } +#if FAST_FALLBACK_INIT_INETSOCK_IMPL == 1 + +void +free_fast_fallback_getaddrinfo_shared(struct fast_fallback_getaddrinfo_shared **shared) +{ + xfree((*shared)->node); + (*shared)->node = NULL; + xfree((*shared)->service); + (*shared)->service = NULL; + rb_nativethread_lock_destroy(&(*shared)->lock); + free(*shared); + *shared = NULL; +} + +static void * +do_fast_fallback_getaddrinfo(void *ptr) +{ + struct fast_fallback_getaddrinfo_entry *entry = (struct fast_fallback_getaddrinfo_entry *)ptr; + struct fast_fallback_getaddrinfo_shared *shared = entry->shared; + int err = 0, shared_need_free = 0; + struct addrinfo *ai = NULL; + + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &set, NULL); + + err = numeric_getaddrinfo(shared->node, shared->service, &entry->hints, &entry->ai); + + if (err != 0) { + err = getaddrinfo(shared->node, shared->service, &entry->hints, &entry->ai); + #ifdef __linux__ + /* On Linux (mainly Ubuntu 13.04) /etc/nsswitch.conf has mdns4 and + * it cause getaddrinfo to return EAI_SYSTEM/ENOENT. [ruby-list:49420] + */ + if (err == EAI_SYSTEM && errno == ENOENT) + err = EAI_NONAME; + #endif + } + + /* for testing HEv2 */ + if (entry->test_sleep_ms > 0) { + struct timespec sleep_ts; + sleep_ts.tv_sec = entry->test_sleep_ms / 1000; + sleep_ts.tv_nsec = (entry->test_sleep_ms % 1000) * 1000000L; + if (sleep_ts.tv_nsec >= 1000000000L) { + sleep_ts.tv_sec += sleep_ts.tv_nsec / 1000000000L; + sleep_ts.tv_nsec = sleep_ts.tv_nsec % 1000000000L; + } + nanosleep(&sleep_ts, NULL); + } + if (entry->test_ecode != 0) { + err = entry->test_ecode; + if (entry->ai) { + freeaddrinfo(entry->ai); + entry->ai = NULL; + } + } + + rb_nativethread_lock_lock(&shared->lock); + { + entry->err = err; + const char notification = entry->family == AF_INET6 ? + IPV6_HOSTNAME_RESOLVED : IPV4_HOSTNAME_RESOLVED; + + if (shared->notify != -1 && (write(shared->notify, ¬ification, 1)) < 0) { + entry->err = errno; + entry->has_syserr = true; + } + if (--(entry->refcount) == 0) { + ai = entry->ai; + entry->ai = NULL; + } + if (--(shared->refcount) == 0) shared_need_free = 1; + } + rb_nativethread_lock_unlock(&shared->lock); + + if (ai) freeaddrinfo(ai); + if (shared_need_free && shared) { + free_fast_fallback_getaddrinfo_shared(&shared); + } + + return 0; +} + +void * +fork_safe_do_fast_fallback_getaddrinfo(void *ptr) +{ + return rb_thread_prevent_fork(do_fast_fallback_getaddrinfo, ptr); +} + +#endif + /* * Addrinfo class */ diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h index 283735b12c..2ec3ab335a 100644 --- a/ext/socket/rubysocket.h +++ b/ext/socket/rubysocket.h @@ -35,6 +35,7 @@ #ifdef _WIN32 # include <winsock2.h> # include <ws2tcpip.h> +# include <mswsock.h> # include <iphlpapi.h> # if defined(_MSC_VER) # undef HAVE_TYPE_STRUCT_SOCKADDR_DL @@ -137,6 +138,7 @@ #include "internal.h" #include "internal/array.h" +#include "internal/compilers.h" #include "internal/error.h" #include "internal/gc.h" #include "internal/io.h" @@ -290,15 +292,13 @@ extern VALUE rb_eResolution; #ifdef SOCKS extern VALUE rb_cSOCKSSocket; # ifndef SOCKS5 -void SOCKSinit(); -int Rconnect(); +void SOCKSinit(char *); +int Rconnect(int, const struct sockaddr *, socklen_t); # endif #endif #include "constdefs.h" -#define BLOCKING_REGION_FD(func, arg) (long)rb_thread_io_blocking_region((func), (arg), (arg)->fd) - #define SockAddrStringValue(v) rsock_sockaddr_string_value(&(v)) #define SockAddrStringValuePtr(v) rsock_sockaddr_string_value_ptr(&(v)) #define SockAddrStringValueWithAddrinfo(v, rai_ret) rsock_sockaddr_string_value_with_addrinfo(&(v), &(rai_ret)) @@ -327,8 +327,8 @@ void rb_freeaddrinfo(struct rb_addrinfo *ai); VALUE rsock_freeaddrinfo(VALUE arg); int rb_getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags); int rsock_fd_family(int fd); -struct rb_addrinfo *rsock_addrinfo(VALUE host, VALUE port, int family, int socktype, int flags); -struct rb_addrinfo *rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_hack); +struct rb_addrinfo *rsock_addrinfo(VALUE host, VALUE port, int family, int socktype, int flags, VALUE timeout); +struct rb_addrinfo *rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_hack, VALUE timeout); VALUE rsock_fd_socket_addrinfo(int fd, struct sockaddr *addr, socklen_t len); VALUE rsock_io_socket_addrinfo(VALUE io, struct sockaddr *addr, socklen_t len); @@ -355,7 +355,7 @@ int rsock_socket(int domain, int type, int proto); int rsock_detect_cloexec(int fd); VALUE rsock_init_sock(VALUE sock, int fd); VALUE rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass); -VALUE rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv, VALUE local_host, VALUE local_serv, int type, VALUE resolv_timeout, VALUE connect_timeout); +VALUE rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv, VALUE local_host, VALUE local_serv, int type, VALUE resolv_timeout, VALUE connect_timeout, VALUE open_timeout, VALUE fast_fallback, VALUE test_mode_settings); VALUE rsock_init_unixsock(VALUE sock, VALUE path, int server); struct rsock_send_arg { @@ -380,7 +380,7 @@ VALUE rsock_s_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str, VALUE ex, enum sock_recv_type from); VALUE rsock_s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from); -int rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks, struct timeval *timeout); +int rsock_connect(VALUE self, const struct sockaddr *sockaddr, int len, int socks, VALUE timeout); VALUE rsock_s_accept(VALUE klass, VALUE io, struct sockaddr *sockaddr, socklen_t *len); VALUE rsock_s_accept_nonblock(VALUE klass, VALUE ex, rb_io_t *fptr, @@ -414,6 +414,47 @@ ssize_t rsock_recvmsg(int socket, struct msghdr *message, int flags); void rsock_discard_cmsg_resource(struct msghdr *mh, int msg_peek_p); #endif +char *raddrinfo_host_str(VALUE host, char *hbuf, size_t hbuflen, int *flags_ptr); +char *raddrinfo_port_str(VALUE port, char *pbuf, size_t pbuflen, int *flags_ptr); + +#ifndef FAST_FALLBACK_INIT_INETSOCK_IMPL +# if !defined(HAVE_PTHREAD_CREATE) || !defined(HAVE_PTHREAD_DETACH) || defined(__MINGW32__) || defined(__MINGW64__) +# define FAST_FALLBACK_INIT_INETSOCK_IMPL 0 +# else +# include "ruby/thread_native.h" +# define FAST_FALLBACK_INIT_INETSOCK_IMPL 1 +# define IPV6_HOSTNAME_RESOLVED '1' +# define IPV4_HOSTNAME_RESOLVED '2' +# define SELECT_CANCELLED '3' + +struct fast_fallback_getaddrinfo_entry +{ + int family, err, refcount; + struct addrinfo hints; + struct addrinfo *ai; + struct fast_fallback_getaddrinfo_shared *shared; + int has_syserr; + long test_sleep_ms; + int test_ecode; +}; + +struct fast_fallback_getaddrinfo_shared +{ + int notify, refcount; + char *node, *service; + rb_nativethread_lock_t lock; + struct fast_fallback_getaddrinfo_entry getaddrinfo_entries[FLEX_ARY_LEN]; +}; + +int raddrinfo_pthread_create(pthread_t *th, void *(*start_routine) (void *), void *arg); +void *fork_safe_do_fast_fallback_getaddrinfo(void *ptr); +void free_fast_fallback_getaddrinfo_entry(struct fast_fallback_getaddrinfo_entry **entry); +void free_fast_fallback_getaddrinfo_shared(struct fast_fallback_getaddrinfo_shared **shared); +# endif +#endif + +NORETURN(void rsock_raise_user_specified_timeout(struct addrinfo *ai, VALUE host, VALUE port)); + void rsock_init_basicsocket(void); void rsock_init_ipsocket(void); void rsock_init_tcpsocket(void); @@ -462,12 +503,12 @@ void rsock_make_fd_nonblock(int fd); int rsock_is_dgram(rb_io_t *fptr); +extern ID tcp_fast_fallback; + #if !defined HAVE_INET_NTOP && ! defined _WIN32 const char *inet_ntop(int, const void *, char *, size_t); #elif defined __MINGW32__ # define inet_ntop(f,a,n,l) rb_w32_inet_ntop(f,a,n,l) -#elif defined _MSC_VER && RUBY_MSVCRT_VERSION < 90 -const char *WSAAPI inet_ntop(int, const void *, char *, size_t); #endif #endif diff --git a/ext/socket/socket.c b/ext/socket/socket.c index c780d77cf6..a8e5ae8119 100644 --- a/ext/socket/socket.c +++ b/ext/socket/socket.c @@ -14,6 +14,8 @@ static VALUE sym_wait_writable; static VALUE sock_s_unpack_sockaddr_in(VALUE, VALUE); +ID tcp_fast_fallback; + void rsock_sys_fail_host_port(const char *mesg, VALUE host, VALUE port) { @@ -387,22 +389,20 @@ rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass) * * connect function in Microsoft's Winsock functions reference */ static VALUE -sock_connect(VALUE sock, VALUE addr) +sock_connect(VALUE self, VALUE addr) { VALUE rai; - rb_io_t *fptr; - int fd, n; SockAddrStringValueWithAddrinfo(addr, rai); addr = rb_str_new4(addr); - GetOpenFile(sock, fptr); - fd = fptr->fd; - n = rsock_connect(fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), 0, NULL); - if (n < 0) { + + int result = rsock_connect(self, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), 0, RUBY_IO_TIMEOUT_DEFAULT); + + if (result < 0) { rsock_sys_fail_raddrinfo_or_sockaddr("connect(2)", addr, rai); } - return INT2FIX(n); + return INT2FIX(result); } /* :nodoc: */ @@ -965,7 +965,7 @@ sock_s_gethostbyname(VALUE obj, VALUE host) { rb_warn("Socket.gethostbyname is deprecated; use Addrinfo.getaddrinfo instead."); struct rb_addrinfo *res = - rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, AI_CANONNAME); + rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, AI_CANONNAME, Qnil); return rsock_make_hostent(host, res, sock_sockaddr); } @@ -1183,7 +1183,7 @@ sock_s_getaddrinfo(int argc, VALUE *argv, VALUE _) norevlookup = rsock_do_not_reverse_lookup; } - res = rsock_getaddrinfo(host, port, &hints, 0); + res = rsock_getaddrinfo(host, port, &hints, 0, Qnil); ret = make_addrinfo(res, norevlookup); rb_freeaddrinfo(res); @@ -1279,7 +1279,7 @@ sock_s_getnameinfo(int argc, VALUE *argv, VALUE _) hints.ai_socktype = (fl & NI_DGRAM) ? SOCK_DGRAM : SOCK_STREAM; /* af */ hints.ai_family = NIL_P(af) ? PF_UNSPEC : rsock_family_arg(af); - res = rsock_getaddrinfo(host, port, &hints, 0); + res = rsock_getaddrinfo(host, port, &hints, 0, Qnil); sap = res->ai->ai_addr; salen = res->ai->ai_addrlen; } @@ -1335,7 +1335,7 @@ sock_s_getnameinfo(int argc, VALUE *argv, VALUE _) static VALUE sock_s_pack_sockaddr_in(VALUE self, VALUE port, VALUE host) { - struct rb_addrinfo *res = rsock_addrinfo(host, port, AF_UNSPEC, 0, 0); + struct rb_addrinfo *res = rsock_addrinfo(host, port, AF_UNSPEC, 0, 0, Qnil); VALUE addr = rb_str_new((char*)res->ai->ai_addr, res->ai->ai_addrlen); rb_freeaddrinfo(res); @@ -1856,6 +1856,67 @@ socket_s_ip_address_list(VALUE self) #define socket_s_ip_address_list rb_f_notimplement #endif +/* + * call-seq: + * Socket.tcp_fast_fallback -> true or false + * + * Returns whether Happy Eyeballs Version 2 ({RFC 8305}[https://datatracker.ietf.org/doc/html/rfc8305]), + * which is provided starting from Ruby 3.4 when using TCPSocket.new and Socket.tcp, + * is enabled or disabled. + * + * If true, it is enabled for TCPSocket.new and Socket.tcp. + * (Note: Happy Eyeballs Version 2 is not provided when using TCPSocket.new on Windows.) + * + * If false, Happy Eyeballs Version 2 is disabled. + * + * For details on Happy Eyeballs Version 2, + * see {Socket.tcp_fast_fallback=}[rdoc-ref:Socket.tcp_fast_fallback=]. + */ +VALUE socket_s_tcp_fast_fallback(VALUE self) { + return rb_ivar_get(rb_cSocket, tcp_fast_fallback); +} + +/* + * call-seq: + * Socket.tcp_fast_fallback= -> true or false + * + * Enable or disable Happy Eyeballs Version 2 ({RFC 8305}[https://datatracker.ietf.org/doc/html/rfc8305]) + * globally, which is provided starting from Ruby 3.4 when using TCPSocket.new and Socket.tcp. + * + * When set to true, the feature is enabled for both `TCPSocket.new` and `Socket.tcp`. + * (Note: This feature is not available when using TCPSocket.new on Windows.) + * + * When set to false, the behavior reverts to that of Ruby 3.3 or earlier. + * + * The default value is true if no value is explicitly set by calling this method. + * However, when the environment variable RUBY_TCP_NO_FAST_FALLBACK=1 is set, + * the default is false. + * + * To control the setting on a per-method basis, use the fast_fallback keyword argument for each method. + * + * === Happy Eyeballs Version 2 + * Happy Eyeballs Version 2 ({RFC 8305}[https://datatracker.ietf.org/doc/html/rfc8305]) + * is an algorithm designed to improve client socket connectivity.<br> + * It aims for more reliable and efficient connections by performing hostname resolution + * and connection attempts in parallel, instead of serially. + * + * Starting from Ruby 3.4, this method operates as follows with this algorithm: + * + * 1. Start resolving both IPv6 and IPv4 addresses concurrently. + * 2. Start connecting to the one of the addresses that are obtained first.<br>If IPv4 addresses are obtained first, + * the method waits 50 ms for IPv6 name resolution to prioritize IPv6 connections. + * 3. After starting a connection attempt, wait 250 ms for the connection to be established.<br> + * If no connection is established within this time, a new connection is started every 250 ms<br> + * until a connection is established or there are no more candidate addresses.<br> + * (Although RFC 8305 strictly specifies sorting addresses,<br> + * this method only alternates between IPv6 / IPv4 addresses due to the performance concerns) + * 4. Once a connection is established, all remaining connection attempts are canceled. + */ +VALUE socket_s_tcp_fast_fallback_set(VALUE self, VALUE value) { + rb_ivar_set(rb_cSocket, tcp_fast_fallback, value); + return value; +} + void Init_socket(void) { @@ -1984,6 +2045,16 @@ Init_socket(void) rsock_init_socket_init(); + const char *tcp_no_fast_fallback_config = getenv("RUBY_TCP_NO_FAST_FALLBACK"); + VALUE fast_fallback_default; + if (tcp_no_fast_fallback_config == NULL || strcmp(tcp_no_fast_fallback_config, "0") == 0) { + fast_fallback_default = Qtrue; + } else { + fast_fallback_default = Qfalse; + } + tcp_fast_fallback = rb_intern_const("tcp_fast_fallback"); + rb_ivar_set(rb_cSocket, tcp_fast_fallback, fast_fallback_default); + rb_define_method(rb_cSocket, "initialize", sock_initialize, -1); rb_define_method(rb_cSocket, "connect", sock_connect, 1); @@ -2027,6 +2098,9 @@ Init_socket(void) rb_define_singleton_method(rb_cSocket, "ip_address_list", socket_s_ip_address_list, 0); + rb_define_singleton_method(rb_cSocket, "tcp_fast_fallback", socket_s_tcp_fast_fallback, 0); + rb_define_singleton_method(rb_cSocket, "tcp_fast_fallback=", socket_s_tcp_fast_fallback_set, 1); + #undef rb_intern sym_wait_writable = ID2SYM(rb_intern("wait_writable")); } diff --git a/ext/socket/sockssocket.c b/ext/socket/sockssocket.c index f263ac3804..30860ea257 100644 --- a/ext/socket/sockssocket.c +++ b/ext/socket/sockssocket.c @@ -30,11 +30,12 @@ socks_init(VALUE sock, VALUE host, VALUE port) static int init = 0; if (init == 0) { - SOCKSinit("ruby"); + char progname[] = "ruby"; + SOCKSinit(progname); init = 1; } - return rsock_init_inetsock(sock, host, port, Qnil, Qnil, INET_SOCKS, Qnil, Qnil); + return rsock_init_inetsock(sock, host, port, Qnil, Qnil, INET_SOCKS, Qnil, Qnil, Qnil, Qfalse, Qnil); } #ifdef SOCKS5 @@ -48,7 +49,7 @@ socks_s_close(VALUE sock) rb_io_t *fptr; GetOpenFile(sock, fptr); - shutdown(fptr->fd, 2); + shutdown(fptr->fd, SHUT_RDWR); return rb_io_close(sock); } #endif diff --git a/ext/socket/tcpserver.c b/ext/socket/tcpserver.c index 04e5a0bb51..0069f3c703 100644 --- a/ext/socket/tcpserver.c +++ b/ext/socket/tcpserver.c @@ -36,7 +36,7 @@ tcp_svr_init(int argc, VALUE *argv, VALUE sock) VALUE hostname, port; rb_scan_args(argc, argv, "011", &hostname, &port); - return rsock_init_inetsock(sock, hostname, port, Qnil, Qnil, INET_SERVER, Qnil, Qnil); + return rsock_init_inetsock(sock, hostname, port, Qnil, Qnil, INET_SERVER, Qnil, Qnil, Qnil, Qfalse, Qnil); } /* diff --git a/ext/socket/tcpsocket.c b/ext/socket/tcpsocket.c index 03787272f3..7ce536e0af 100644 --- a/ext/socket/tcpsocket.c +++ b/ext/socket/tcpsocket.c @@ -12,13 +12,31 @@ /* * call-seq: - * TCPSocket.new(remote_host, remote_port, local_host=nil, local_port=nil, connect_timeout: nil) + * TCPSocket.new(remote_host, remote_port, local_host=nil, local_port=nil, resolv_timeout: nil, connect_timeout: nil, open_timeout: nil, fast_fallback: true) * * Opens a TCP connection to +remote_host+ on +remote_port+. If +local_host+ * and +local_port+ are specified, then those parameters are used on the local * end to establish the connection. * - * [:connect_timeout] specify the timeout in seconds. + * Starting from Ruby 3.4, this method operates according to the + * Happy Eyeballs Version 2 ({RFC 8305}[https://datatracker.ietf.org/doc/html/rfc8305]) + * algorithm by default, except on Windows. + * + * For details on Happy Eyeballs Version 2, + * see {Socket.tcp_fast_fallback=}[rdoc-ref:Socket.tcp_fast_fallback=]. + * + * To make it behave the same as in Ruby 3.3 and earlier, + * explicitly specify the option fast_fallback:false. + * Or, setting Socket.tcp_fast_fallback=false will disable + * Happy Eyeballs Version 2 not only for this method but for all Socket globally. + * + * When using TCPSocket.new on Windows, Happy Eyeballs Version 2 is not provided, + * and it behaves the same as in Ruby 3.3 and earlier. + * + * [:resolv_timeout] Specifies the timeout in seconds from when the hostname resolution starts. + * [:connect_timeout] This method sequentially attempts connecting to all candidate destination addresses.<br>The +connect_timeout+ specifies the timeout in seconds from the start of the connection attempt to the last candidate.<br>By default, all connection attempts continue until the timeout occurs.<br>When +fast_fallback:false+ is explicitly specified,<br>a timeout is set for each connection attempt and any connection attempt that exceeds its timeout will be canceled. + * [:open_timeout] Specifies the timeout in seconds from the start of the method execution.<br>If this timeout is reached while there are still addresses that have not yet been attempted for connection, no further attempts will be made.<br>If this option is specified together with other timeout options, an +ArgumentError+ will be raised. + * [:fast_fallback] Enables the Happy Eyeballs Version 2 algorithm (enabled by default). */ static VALUE tcp_init(int argc, VALUE *argv, VALUE sock) @@ -26,28 +44,43 @@ tcp_init(int argc, VALUE *argv, VALUE sock) VALUE remote_host, remote_serv; VALUE local_host, local_serv; VALUE opt; - static ID keyword_ids[2]; - VALUE kwargs[2]; + static ID keyword_ids[5]; + VALUE kwargs[5]; VALUE resolv_timeout = Qnil; VALUE connect_timeout = Qnil; + VALUE open_timeout = Qnil; + VALUE fast_fallback = Qnil; + VALUE test_mode_settings = Qnil; if (!keyword_ids[0]) { CONST_ID(keyword_ids[0], "resolv_timeout"); CONST_ID(keyword_ids[1], "connect_timeout"); + CONST_ID(keyword_ids[2], "open_timeout"); + CONST_ID(keyword_ids[3], "fast_fallback"); + CONST_ID(keyword_ids[4], "test_mode_settings"); } rb_scan_args(argc, argv, "22:", &remote_host, &remote_serv, &local_host, &local_serv, &opt); if (!NIL_P(opt)) { - rb_get_kwargs(opt, keyword_ids, 0, 2, kwargs); + rb_get_kwargs(opt, keyword_ids, 0, 5, kwargs); if (kwargs[0] != Qundef) { resolv_timeout = kwargs[0]; } if (kwargs[1] != Qundef) { connect_timeout = kwargs[1]; } + if (kwargs[2] != Qundef) { open_timeout = kwargs[2]; } + if (kwargs[3] != Qundef) { fast_fallback = kwargs[3]; } + if (kwargs[4] != Qundef) { test_mode_settings = kwargs[4]; } + } + + if (fast_fallback == Qnil) { + fast_fallback = rb_ivar_get(rb_cSocket, tcp_fast_fallback); + if (fast_fallback == Qnil) fast_fallback = Qtrue; } return rsock_init_inetsock(sock, remote_host, remote_serv, local_host, local_serv, INET_CLIENT, - resolv_timeout, connect_timeout); + resolv_timeout, connect_timeout, open_timeout, + fast_fallback, test_mode_settings); } static VALUE @@ -80,7 +113,7 @@ tcp_s_gethostbyname(VALUE obj, VALUE host) { rb_warn("TCPSocket.gethostbyname is deprecated; use Addrinfo.getaddrinfo instead."); struct rb_addrinfo *res = - rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, AI_CANONNAME); + rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, AI_CANONNAME, Qnil); return rsock_make_hostent(host, res, tcp_sockaddr); } diff --git a/ext/socket/udpsocket.c b/ext/socket/udpsocket.c index 5224e48a96..b2bc925538 100644 --- a/ext/socket/udpsocket.c +++ b/ext/socket/udpsocket.c @@ -45,22 +45,18 @@ udp_init(int argc, VALUE *argv, VALUE sock) struct udp_arg { + VALUE io; struct rb_addrinfo *res; - rb_io_t *fptr; }; static VALUE udp_connect_internal(VALUE v) { struct udp_arg *arg = (void *)v; - rb_io_t *fptr; - int fd; struct addrinfo *res; - rb_io_check_closed(fptr = arg->fptr); - fd = fptr->fd; for (res = arg->res->ai; res; res = res->ai_next) { - if (rsock_connect(fd, res->ai_addr, res->ai_addrlen, 0, NULL) >= 0) { + if (rsock_connect(arg->io, res->ai_addr, res->ai_addrlen, 0, RUBY_IO_TIMEOUT_DEFAULT) >= 0) { return Qtrue; } } @@ -84,16 +80,17 @@ udp_connect_internal(VALUE v) * */ static VALUE -udp_connect(VALUE sock, VALUE host, VALUE port) +udp_connect(VALUE self, VALUE host, VALUE port) { - struct udp_arg arg; - VALUE ret; + struct udp_arg arg = {.io = self}; + + arg.res = rsock_addrinfo(host, port, rsock_fd_family(rb_io_descriptor(self)), SOCK_DGRAM, 0, Qnil); + + int result = (int)rb_ensure(udp_connect_internal, (VALUE)&arg, rsock_freeaddrinfo, (VALUE)arg.res); + if (!result) { + rsock_sys_fail_host_port("connect(2)", host, port); + } - GetOpenFile(sock, arg.fptr); - arg.res = rsock_addrinfo(host, port, rsock_fd_family(arg.fptr->fd), SOCK_DGRAM, 0); - ret = rb_ensure(udp_connect_internal, (VALUE)&arg, - rsock_freeaddrinfo, (VALUE)arg.res); - if (!ret) rsock_sys_fail_host_port("connect(2)", host, port); return INT2FIX(0); } @@ -101,14 +98,13 @@ static VALUE udp_bind_internal(VALUE v) { struct udp_arg *arg = (void *)v; - rb_io_t *fptr; - int fd; struct addrinfo *res; - rb_io_check_closed(fptr = arg->fptr); - fd = fptr->fd; + rb_io_t *fptr; + RB_IO_POINTER(arg->io, fptr); + for (res = arg->res->ai; res; res = res->ai_next) { - if (bind(fd, res->ai_addr, res->ai_addrlen) < 0) { + if (bind(fptr->fd, res->ai_addr, res->ai_addrlen) < 0) { continue; } return Qtrue; @@ -129,22 +125,23 @@ udp_bind_internal(VALUE v) * */ static VALUE -udp_bind(VALUE sock, VALUE host, VALUE port) +udp_bind(VALUE self, VALUE host, VALUE port) { - struct udp_arg arg; - VALUE ret; + struct udp_arg arg = {.io = self}; + + arg.res = rsock_addrinfo(host, port, rsock_fd_family(rb_io_descriptor(self)), SOCK_DGRAM, 0, Qnil); + + VALUE result = rb_ensure(udp_bind_internal, (VALUE)&arg, rsock_freeaddrinfo, (VALUE)arg.res); + if (!result) { + rsock_sys_fail_host_port("bind(2)", host, port); + } - GetOpenFile(sock, arg.fptr); - arg.res = rsock_addrinfo(host, port, rsock_fd_family(arg.fptr->fd), SOCK_DGRAM, 0); - ret = rb_ensure(udp_bind_internal, (VALUE)&arg, - rsock_freeaddrinfo, (VALUE)arg.res); - if (!ret) rsock_sys_fail_host_port("bind(2)", host, port); return INT2FIX(0); } struct udp_send_arg { - struct rb_addrinfo *res; rb_io_t *fptr; + struct rb_addrinfo *res; struct rsock_send_arg sarg; }; @@ -166,7 +163,7 @@ udp_send_internal(VALUE v) rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_WRITABLE), Qnil); #endif - ssize_t n = (ssize_t)BLOCKING_REGION_FD(rsock_sendto_blocking, &arg->sarg); + ssize_t n = (ssize_t)rb_io_blocking_region(fptr, rsock_sendto_blocking, &arg->sarg); if (n >= 0) return RB_SSIZE2NUM(n); @@ -215,7 +212,7 @@ udp_send(int argc, VALUE *argv, VALUE sock) GetOpenFile(sock, arg.fptr); arg.sarg.fd = arg.fptr->fd; arg.sarg.flags = NUM2INT(flags); - arg.res = rsock_addrinfo(host, port, rsock_fd_family(arg.fptr->fd), SOCK_DGRAM, 0); + arg.res = rsock_addrinfo(host, port, rsock_fd_family(arg.fptr->fd), SOCK_DGRAM, 0, Qnil); ret = rb_ensure(udp_send_internal, (VALUE)&arg, rsock_freeaddrinfo, (VALUE)arg.res); if (!ret) rsock_sys_fail_host_port("sendto(2)", host, port); diff --git a/ext/socket/unixsocket.c b/ext/socket/unixsocket.c index a8475e3e60..2ec9376074 100644 --- a/ext/socket/unixsocket.c +++ b/ext/socket/unixsocket.c @@ -14,15 +14,14 @@ struct unixsock_arg { struct sockaddr_un *sockaddr; socklen_t sockaddrlen; - int fd; + VALUE io; }; static VALUE unixsock_connect_internal(VALUE a) { struct unixsock_arg *arg = (struct unixsock_arg *)a; - return (VALUE)rsock_connect(arg->fd, (struct sockaddr*)arg->sockaddr, - arg->sockaddrlen, 0, NULL); + return (VALUE)rsock_connect(arg->io, (struct sockaddr*)arg->sockaddr, arg->sockaddrlen, 0, RUBY_IO_TIMEOUT_DEFAULT); } static VALUE @@ -43,15 +42,16 @@ unixsock_path_value(VALUE path) } } #endif + path = rb_get_path(path); #ifdef _WIN32 /* UNIXSocket requires UTF-8 per spec. */ path = rb_str_export_to_enc(path, rb_utf8_encoding()); #endif - return rb_get_path(path); + return path; } VALUE -rsock_init_unixsock(VALUE sock, VALUE path, int server) +rsock_init_unixsock(VALUE self, VALUE path, int server) { struct sockaddr_un sockaddr; socklen_t sockaddrlen; @@ -73,43 +73,46 @@ rsock_init_unixsock(VALUE sock, VALUE path, int server) rsock_sys_fail_path("socket(2)", path); } + VALUE io = rsock_init_sock(self, fd); + RB_IO_POINTER(io, fptr); + if (server) { status = bind(fd, (struct sockaddr*)&sockaddr, sockaddrlen); } else { - int prot; + int error_tag; struct unixsock_arg arg; arg.sockaddr = &sockaddr; arg.sockaddrlen = sockaddrlen; - arg.fd = fd; - status = (int)rb_protect(unixsock_connect_internal, (VALUE)&arg, &prot); - if (prot) { - close(fd); - rb_jump_tag(prot); + arg.io = io; + + status = (int)rb_protect(unixsock_connect_internal, (VALUE)&arg, &error_tag); + + if (error_tag) { + rb_io_close(io); + rb_jump_tag(error_tag); } } if (status < 0) { int e = errno; - close(fd); + rb_io_close(io); rsock_syserr_fail_path(e, "connect(2)", path); } if (server) { if (listen(fd, SOMAXCONN) < 0) { int e = errno; - close(fd); + rb_io_close(io); rsock_syserr_fail_path(e, "listen(2)", path); } } - rsock_init_sock(sock, fd); if (server) { - GetOpenFile(sock, fptr); fptr->pathv = rb_str_new_frozen(path); } - return sock; + return io; } /* @@ -125,9 +128,9 @@ rsock_init_unixsock(VALUE sock, VALUE path, int server) * */ static VALUE -unix_init(VALUE sock, VALUE path) +unix_init(VALUE self, VALUE path) { - return rsock_init_unixsock(sock, path, 0); + return rsock_init_unixsock(self, path, 0); } /* @@ -288,7 +291,7 @@ unix_send_io(VALUE sock, VALUE val) #endif arg.fd = fptr->fd; - while ((int)BLOCKING_REGION_FD(sendmsg_blocking, &arg) == -1) { + while ((int)rb_io_blocking_region(fptr, sendmsg_blocking, &arg) == -1) { if (!rb_io_wait_writable(arg.fd)) rsock_sys_fail_path("sendmsg(2)", fptr->pathv); } @@ -390,7 +393,7 @@ retry: #endif arg.fd = fptr->fd; - while ((int)BLOCKING_REGION_FD(recvmsg_blocking, &arg) == -1) { + while ((int)rb_io_blocking_region(fptr, recvmsg_blocking, &arg) == -1) { int e = errno; if (e == EMSGSIZE && !(gc_reason & GC_REASON_EMSGSIZE)) { /* FreeBSD gets here when we're out of FDs */ diff --git a/ext/win32ole/.document b/ext/stringio/.document index decba0135a..decba0135a 100644 --- a/ext/win32ole/.document +++ b/ext/stringio/.document diff --git a/ext/stringio/depend b/ext/stringio/depend index ba2b812041..3a82ad0a11 100644 --- a/ext/stringio/depend +++ b/ext/stringio/depend @@ -138,6 +138,7 @@ stringio.o: $(hdrdir)/ruby/internal/intern/re.h stringio.o: $(hdrdir)/ruby/internal/intern/ruby.h stringio.o: $(hdrdir)/ruby/internal/intern/select.h stringio.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +stringio.o: $(hdrdir)/ruby/internal/intern/set.h stringio.o: $(hdrdir)/ruby/internal/intern/signal.h stringio.o: $(hdrdir)/ruby/internal/intern/sprintf.h stringio.o: $(hdrdir)/ruby/internal/intern/string.h @@ -157,6 +158,7 @@ stringio.o: $(hdrdir)/ruby/internal/special_consts.h stringio.o: $(hdrdir)/ruby/internal/static_assert.h stringio.o: $(hdrdir)/ruby/internal/stdalign.h stringio.o: $(hdrdir)/ruby/internal/stdbool.h +stringio.o: $(hdrdir)/ruby/internal/stdckdint.h stringio.o: $(hdrdir)/ruby/internal/symbol.h stringio.o: $(hdrdir)/ruby/internal/value.h stringio.o: $(hdrdir)/ruby/internal/value_type.h @@ -170,5 +172,6 @@ stringio.o: $(hdrdir)/ruby/oniguruma.h stringio.o: $(hdrdir)/ruby/ruby.h stringio.o: $(hdrdir)/ruby/st.h stringio.o: $(hdrdir)/ruby/subst.h +stringio.o: $(hdrdir)/ruby/version.h stringio.o: stringio.c # AUTOGENERATED DEPENDENCIES END diff --git a/ext/stringio/extconf.rb b/ext/stringio/extconf.rb index 553732f79c..0089766983 100644 --- a/ext/stringio/extconf.rb +++ b/ext/stringio/extconf.rb @@ -1,6 +1,8 @@ # frozen_string_literal: false require 'mkmf' if RUBY_ENGINE == 'ruby' + have_type("rb_io_mode_t", "ruby/io.h") + create_makefile('stringio') else File.write('Makefile', dummy_makefile("").join) diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c index 27c7f65408..cc2294a795 100644 --- a/ext/stringio/stringio.c +++ b/ext/stringio/stringio.c @@ -13,11 +13,14 @@ **********************************************************************/ static const char *const -STRINGIO_VERSION = "3.1.1"; +STRINGIO_VERSION = "3.2.1.dev"; + +#include <stdbool.h> #include "ruby.h" #include "ruby/io.h" #include "ruby/encoding.h" +#include "ruby/version.h" #if defined(HAVE_FCNTL_H) || defined(_WIN32) #include <fcntl.h> #elif defined(HAVE_SYS_FCNTL_H) @@ -33,12 +36,29 @@ STRINGIO_VERSION = "3.1.1"; # define rb_class_new_instance_kw(argc, argv, klass, kw_splat) rb_class_new_instance(argc, argv, klass) #endif +static inline bool +str_chilled_p(VALUE str) +{ +#if (RUBY_API_VERSION_MAJOR == 3 && RUBY_API_VERSION_MINOR >= 4) || RUBY_API_VERSION_MAJOR >= 4 + // Do not attempt to modify chilled strings on Ruby 3.4+ + // RUBY_FL_USER2 == STR_CHILLED_LITERAL + // RUBY_FL_USER3 == STR_CHILLED_SYMBOL_TO_S + return FL_TEST_RAW(str, RUBY_FL_USER2 | RUBY_FL_USER3); +#else + return false; +#endif +} + +#ifndef HAVE_TYPE_RB_IO_MODE_T +typedef int rb_io_mode_t; +#endif + struct StringIO { VALUE string; rb_encoding *enc; long pos; long lineno; - int flags; + rb_io_mode_t flags; int count; }; @@ -48,7 +68,13 @@ static long strio_write(VALUE self, VALUE str); #define IS_STRIO(obj) (rb_typeddata_is_kind_of((obj), &strio_data_type)) #define error_inval(msg) (rb_syserr_fail(EINVAL, msg)) -#define get_enc(ptr) ((ptr)->enc ? (ptr)->enc : rb_enc_get((ptr)->string)) +#define get_enc(ptr) ((ptr)->enc ? (ptr)->enc : !NIL_P((ptr)->string) ? rb_enc_get((ptr)->string) : NULL) + +static bool +readonly_string_p(VALUE string) +{ + return OBJ_FROZEN_RAW(string); +} static struct StringIO * strio_alloc(void) @@ -166,9 +192,27 @@ writable(VALUE strio) static void check_modifiable(struct StringIO *ptr) { - if (OBJ_FROZEN(ptr->string)) { + if (NIL_P(ptr->string)) { + /* Null device StringIO */ + } + else if (OBJ_FROZEN_RAW(ptr->string)) { rb_raise(rb_eIOError, "not modifiable string"); } + else { + rb_str_modify(ptr->string); + } +} + +static inline bool +outside_p(struct StringIO *ptr, long pos) +{ + return NIL_P(ptr->string) || pos >= RSTRING_LEN(ptr->string); +} + +static inline bool +eos_p(struct StringIO *ptr) +{ + return outside_p(ptr, ptr->pos); } static VALUE @@ -181,17 +225,32 @@ strio_s_allocate(VALUE klass) * call-seq: * StringIO.new(string = '', mode = 'r+') -> new_stringio * - * Note that +mode+ defaults to <tt>'r'</tt> if +string+ is frozen. - * * Returns a new \StringIO instance formed from +string+ and +mode+; - * see {Access Modes}[rdoc-ref:File@Access+Modes]: + * the instance should be closed when no longer needed: + * + * strio = StringIO.new + * strio.string # => "" + * strio.closed_read? # => false + * strio.closed_write? # => false + * strio.close + * + * If +string+ is frozen, the default +mode+ is <tt>'r'</tt>: * - * strio = StringIO.new # => #<StringIO> + * strio = StringIO.new('foo'.freeze) + * strio.string # => "foo" + * strio.closed_read? # => false + * strio.closed_write? # => true * strio.close * - * The instance should be closed when no longer needed. + * Argument +mode+ must be a valid + * {Access Mode}[rdoc-ref:File@Access+Modes], + * which may be a string or an integer constant: * - * Related: StringIO.open (accepts block; closes automatically). + * StringIO.new('foo', 'w+') + * StringIO.new('foo', File::RDONLY) + * + * Related: StringIO.open + * (passes the \StringIO object to the block; closes the object automatically on block exit). */ static VALUE strio_initialize(int argc, VALUE *argv, VALUE self) @@ -281,13 +340,14 @@ strio_init(int argc, VALUE *argv, struct StringIO *ptr, VALUE self) argc = rb_scan_args(argc, argv, "02:", &string, &vmode, &opt); rb_io_extract_modeenc(&vmode, 0, opt, &oflags, &ptr->flags, &convconfig); - if (argc) { + if (!NIL_P(string)) { StringValue(string); } - else { + else if (!argc) { string = rb_enc_str_new("", 0, rb_default_external_encoding()); } - if (OBJ_FROZEN_RAW(string)) { + + if (!NIL_P(string) && readonly_string_p(string)) { if (ptr->flags & FMODE_WRITABLE) { rb_syserr_fail(EACCES, 0); } @@ -297,11 +357,11 @@ strio_init(int argc, VALUE *argv, struct StringIO *ptr, VALUE self) ptr->flags |= FMODE_WRITABLE; } } - if (ptr->flags & FMODE_TRUNC) { + if (!NIL_P(string) && (ptr->flags & FMODE_TRUNC)) { rb_str_resize(string, 0); } RB_OBJ_WRITE(self, &ptr->string, string); - if (argc == 1) { + if (argc == 1 && !NIL_P(string)) { ptr->enc = rb_enc_get(string); } else { @@ -325,23 +385,20 @@ strio_finalize(VALUE self) /* * call-seq: - * StringIO.open(string = '', mode = 'r+') {|strio| ... } + * StringIO.open(string = '', mode = 'r+') -> new_stringio + * StringIO.open(string = '', mode = 'r+') {|strio| ... } -> object * - * Note that +mode+ defaults to <tt>'r'</tt> if +string+ is frozen. + * Creates new \StringIO instance by calling <tt>StringIO.new(string, mode)</tt>. * - * Creates a new \StringIO instance formed from +string+ and +mode+; - * see {Access Modes}[rdoc-ref:File@Access+Modes]. - * - * With no block, returns the new instance: + * With no block given, returns the new instance: * * strio = StringIO.open # => #<StringIO> * - * With a block, calls the block with the new instance + * With a block given, calls the block with the new instance * and returns the block's value; - * closes the instance on block exit. + * closes the instance on block exit: * - * StringIO.open {|strio| p strio } - * # => #<StringIO> + * StringIO.open('foo') {|strio| strio.string.upcase } # => "FOO" * * Related: StringIO.new. */ @@ -367,7 +424,7 @@ strio_s_new(int argc, VALUE *argv, VALUE klass) } /* - * Returns +false+. Just for compatibility to IO. + * Returns +false+; for compatibility with IO. */ static VALUE strio_false(VALUE self) @@ -377,7 +434,7 @@ strio_false(VALUE self) } /* - * Returns +nil+. Just for compatibility to IO. + * Returns +nil+; for compatibility with IO. */ static VALUE strio_nil(VALUE self) @@ -387,7 +444,7 @@ strio_nil(VALUE self) } /* - * Returns an object itself. Just for compatibility to IO. + * Returns +self+; for compatibility with IO. */ static VALUE strio_self(VALUE self) @@ -397,7 +454,7 @@ strio_self(VALUE self) } /* - * Returns 0. Just for compatibility to IO. + * Returns 0; for compatibility with IO. */ static VALUE strio_0(VALUE self) @@ -457,7 +514,7 @@ strio_get_string(VALUE self) * call-seq: * string = other_string -> other_string * - * Assigns the underlying string as +other_string+, and sets position to zero; + * Replaces the stored string with +other_string+, and sets the position to zero; * returns +other_string+: * * StringIO.open('foo') do |strio| @@ -471,7 +528,7 @@ strio_get_string(VALUE self) * "foo" * "bar" * - * Related: StringIO#string (returns the underlying string). + * Related: StringIO#string (returns the stored string). */ static VALUE strio_set_string(VALUE self, VALUE string) @@ -481,7 +538,7 @@ strio_set_string(VALUE self, VALUE string) rb_io_taint_check(self); ptr->flags &= ~FMODE_READWRITE; StringValue(string); - ptr->flags = OBJ_FROZEN(string) ? FMODE_READABLE : FMODE_READWRITE; + ptr->flags = readonly_string_p(string) ? FMODE_READABLE : FMODE_READWRITE; ptr->pos = 0; ptr->lineno = 0; RB_OBJ_WRITE(self, &ptr->string, string); @@ -492,11 +549,16 @@ strio_set_string(VALUE self, VALUE string) * call-seq: * close -> nil * - * Closes +self+ for both reading and writing. + * Closes +self+ for both reading and writing; returns +nil+: * - * Raises IOError if reading or writing is attempted. + * strio = StringIO.new + * strio.closed? # => false + * strio.close # => nil + * strio.closed? # => true + * strio.read # Raises IOError: not opened for reading + * strio.write # Raises IOError: not opened for writing * - * Related: StringIO#close_read, StringIO#close_write. + * Related: StringIO#close_read, StringIO#close_write, StringIO.closed?. */ static VALUE strio_close(VALUE self) @@ -510,9 +572,16 @@ strio_close(VALUE self) * call-seq: * close_read -> nil * - * Closes +self+ for reading; closed-write setting remains unchanged. + * Closes +self+ for reading; + * closed-write setting remains unchanged; + * returns +nil+: * - * Raises IOError if reading is attempted. + * strio = StringIO.new + * strio.closed_read? # => false + * strio.close_read # => nil + * strio.closed_read? # => true + * strio.closed_write? # => false + * strio.read # Raises IOError: not opened for reading * * Related: StringIO#close, StringIO#close_write. */ @@ -531,11 +600,16 @@ strio_close_read(VALUE self) * call-seq: * close_write -> nil * - * Closes +self+ for writing; closed-read setting remains unchanged. + * Closes +self+ for writing; closed-read setting remains unchanged; returns +nil+: * - * Raises IOError if writing is attempted. + * strio = StringIO.new + * strio.closed_write? # => false + * strio.close_write # => nil + * strio.closed_write? # => true + * strio.closed_read? # => false + * strio.write('foo') # Raises IOError: not opened for writing * - * Related: StringIO#close, StringIO#close_read. + * Related: StringIO#close, StringIO#close_read, StringIO#closed_write?. */ static VALUE strio_close_write(VALUE self) @@ -552,8 +626,16 @@ strio_close_write(VALUE self) * call-seq: * closed? -> true or false * - * Returns +true+ if +self+ is closed for both reading and writing, - * +false+ otherwise. + * Returns whether +self+ is closed for both reading and writing: + * + * strio = StringIO.new + * strio.closed? # => false # Open for reading and writing. + * strio.close_read + * strio.closed? # => false # Still open for writing. + * strio.close_write + * strio.closed? # => true # Now closed for both. + * + * Related: StringIO.closed_read?, StringIO.closed_write?. */ static VALUE strio_closed(VALUE self) @@ -567,7 +649,14 @@ strio_closed(VALUE self) * call-seq: * closed_read? -> true or false * - * Returns +true+ if +self+ is closed for reading, +false+ otherwise. + * Returns whether +self+ is closed for reading: + * + * strio = StringIO.new + * strio.closed_read? # => false + * strio.close_read + * strio.closed_read? # => true + * + * Related: StringIO#closed?, StringIO#closed_write?, StringIO#close_read. */ static VALUE strio_closed_read(VALUE self) @@ -581,7 +670,14 @@ strio_closed_read(VALUE self) * call-seq: * closed_write? -> true or false * - * Returns +true+ if +self+ is closed for writing, +false+ otherwise. + * Returns whether +self+ is closed for writing: + * + * strio = StringIO.new + * strio.closed_write? # => false + * strio.close_write + * strio.closed_write? # => true + * + * Related: StringIO#close_write, StringIO#closed?, StringIO#closed_read?. */ static VALUE strio_closed_write(VALUE self) @@ -595,18 +691,26 @@ static struct StringIO * strio_to_read(VALUE self) { struct StringIO *ptr = readable(self); - if (ptr->pos < RSTRING_LEN(ptr->string)) return ptr; - return NULL; + if (eos_p(ptr)) return NULL; + return ptr; } /* * call-seq: * eof? -> true or false * - * Returns +true+ if positioned at end-of-stream, +false+ otherwise; - * see {Position}[rdoc-ref:IO@Position]. + * Returns whether +self+ is positioned at end-of-stream: + * + * strio = StringIO.new('foo') + * strio.pos # => 0 + * strio.eof? # => false + * strio.read # => "foo" + * strio.pos # => 3 + * strio.eof? # => true + * strio.close_read + * strio.eof? # Raises IOError: not opened for reading * - * Raises IOError if the stream is not opened for reading. + * Related: StringIO#pos. */ static VALUE strio_eof(VALUE self) @@ -643,7 +747,7 @@ strio_copy(VALUE copy, VALUE orig) * lineno -> current_line_number * * Returns the current line number in +self+; - * see {Line Number}[rdoc-ref:IO@Line+Number]. + * see {Line Number}[rdoc-ref:StringIO@Line+Number]. */ static VALUE strio_get_lineno(VALUE self) @@ -656,7 +760,7 @@ strio_get_lineno(VALUE self) * lineno = new_line_number -> new_line_number * * Sets the current line number in +self+ to the given +new_line_number+; - * see {Line Number}[rdoc-ref:IO@Line+Number]. + * see {Line Number}[rdoc-ref:StringIO@Line+Number]. */ static VALUE strio_set_lineno(VALUE self, VALUE lineno) @@ -670,7 +774,7 @@ strio_set_lineno(VALUE self, VALUE lineno) * binmode -> self * * Sets the data mode in +self+ to binary mode; - * see {Data Mode}[rdoc-ref:File@Data+Mode]. + * see {Data Mode}[rdoc-ref:StringIO@Data+Mode]. * */ static VALUE @@ -731,7 +835,7 @@ strio_reopen(int argc, VALUE *argv, VALUE self) * pos -> stream_position * * Returns the current position (in bytes); - * see {Position}[rdoc-ref:IO@Position]. + * see {Position}[rdoc-ref:StringIO@Position]. */ static VALUE strio_get_pos(VALUE self) @@ -744,7 +848,7 @@ strio_get_pos(VALUE self) * pos = new_position -> new_position * * Sets the current position (in bytes); - * see {Position}[rdoc-ref:IO@Position]. + * see {Position}[rdoc-ref:StringIO@Position]. */ static VALUE strio_set_pos(VALUE self, VALUE pos) @@ -779,9 +883,9 @@ strio_rewind(VALUE self) * call-seq: * seek(offset, whence = SEEK_SET) -> 0 * - * Sets the current position to the given integer +offset+ (in bytes), + * Sets the position to the given integer +offset+ (in bytes), * with respect to a given constant +whence+; - * see {Position}[rdoc-ref:IO@Position]. + * see {IO#seek}[rdoc-ref:IO#seek]. */ static VALUE strio_seek(int argc, VALUE *argv, VALUE self) @@ -803,7 +907,11 @@ strio_seek(int argc, VALUE *argv, VALUE self) offset = ptr->pos; break; case 2: - offset = RSTRING_LEN(ptr->string); + if (NIL_P(ptr->string)) { + offset = 0; + } else { + offset = RSTRING_LEN(ptr->string); + } break; default: error_inval("invalid whence"); @@ -836,10 +944,9 @@ strio_get_sync(VALUE self) * call-seq: * each_byte {|byte| ... } -> self * - * With a block given, calls the block with each remaining byte in the stream; - * see {Byte IO}[rdoc-ref:IO@Byte+IO]. + * :include: stringio/each_byte.rdoc * - * With no block given, returns an enumerator. + * Related: StringIO#each_char, StringIO#each_codepoint, StringIO#each_line. */ static VALUE strio_each_byte(VALUE self) @@ -857,10 +964,10 @@ strio_each_byte(VALUE self) /* * call-seq: - * getc -> character or nil + * getc -> character, byte, or nil + * + * :include: stringio/getc.rdoc * - * Reads and returns the next character from the stream; - * see {Character IO}[rdoc-ref:IO@Character+IO]. */ static VALUE strio_getc(VALUE self) @@ -872,7 +979,7 @@ strio_getc(VALUE self) int len; char *p; - if (pos >= RSTRING_LEN(str)) { + if (eos_p(ptr)) { return Qnil; } p = RSTRING_PTR(str)+pos; @@ -883,17 +990,17 @@ strio_getc(VALUE self) /* * call-seq: - * getbyte -> byte or nil + * getbyte -> integer or nil + * + * :include: stringio/getbyte.rdoc * - * Reads and returns the next 8-bit byte from the stream; - * see {Byte IO}[rdoc-ref:IO@Byte+IO]. */ static VALUE strio_getbyte(VALUE self) { struct StringIO *ptr = readable(self); int c; - if (ptr->pos >= RSTRING_LEN(ptr->string)) { + if (eos_p(ptr)) { return Qnil; } c = RSTRING_PTR(ptr->string)[ptr->pos++]; @@ -917,6 +1024,18 @@ strio_extend(struct StringIO *ptr, long pos, long len) } } +static void +strio_unget_string(struct StringIO *ptr, VALUE c) +{ + const char *cp = NULL; + long cl = RSTRING_LEN(c); + if (cl > 0) { + if (c != ptr->string) cp = RSTRING_PTR(c); + strio_unget_bytes(ptr, cp, cl); + RB_GC_GUARD(c); + } +} + /* * call-seq: * ungetc(character) -> nil @@ -931,6 +1050,7 @@ strio_ungetc(VALUE self, VALUE c) rb_encoding *enc, *enc2; check_modifiable(ptr); + if (NIL_P(ptr->string)) return Qnil; if (NIL_P(c)) return Qnil; if (RB_INTEGER_TYPE_P(c)) { int len, cc = NUM2INT(c); @@ -938,19 +1058,22 @@ strio_ungetc(VALUE self, VALUE c) enc = rb_enc_get(ptr->string); len = rb_enc_codelen(cc, enc); - if (len <= 0) rb_enc_uint_chr(cc, enc); + if (len <= 0) { + rb_enc_uint_chr(cc, enc); /* to raise an exception */ + UNREACHABLE; + } rb_enc_mbcput(cc, buf, enc); return strio_unget_bytes(ptr, buf, len); } else { - SafeStringValue(c); + StringValue(c); + if (RSTRING_LEN(c) == 0) return Qnil; enc = rb_enc_get(ptr->string); enc2 = rb_enc_get(c); if (enc != enc2 && enc != rb_ascii8bit_encoding()) { c = rb_str_conv_enc(c, enc2, enc); } - strio_unget_bytes(ptr, RSTRING_PTR(c), RSTRING_LEN(c)); - RB_GC_GUARD(c); + strio_unget_string(ptr, c); return Qnil; } } @@ -968,21 +1091,17 @@ strio_ungetbyte(VALUE self, VALUE c) struct StringIO *ptr = readable(self); check_modifiable(ptr); + if (NIL_P(ptr->string)) return Qnil; if (NIL_P(c)) return Qnil; if (RB_INTEGER_TYPE_P(c)) { - /* rb_int_and() not visible from exts */ - VALUE v = rb_funcall(c, '&', 1, INT2FIX(0xff)); - const char cc = NUM2INT(v) & 0xFF; - strio_unget_bytes(ptr, &cc, 1); + /* rb_int_and() not visible from exts */ + VALUE v = rb_funcall(c, '&', 1, INT2FIX(0xff)); + const char cc = NUM2INT(v) & 0xFF; + strio_unget_bytes(ptr, &cc, 1); } else { - long cl; - SafeStringValue(c); - cl = RSTRING_LEN(c); - if (cl > 0) { - strio_unget_bytes(ptr, RSTRING_PTR(c), cl); - RB_GC_GUARD(c); - } + StringValue(c); + strio_unget_string(ptr, c); } return Qnil; } @@ -1013,7 +1132,7 @@ strio_unget_bytes(struct StringIO *ptr, const char *cp, long cl) if (rest > cl) memset(s + len, 0, rest - cl); pos -= cl; } - memcpy(s + pos, cp, cl); + memcpy(s + pos, (cp ? cp : s), cl); ptr->pos = pos; return Qnil; } @@ -1050,12 +1169,11 @@ strio_readbyte(VALUE self) /* * call-seq: - * each_char {|c| ... } -> self + * each_char {|char| ... } -> self * - * With a block given, calls the block with each remaining character in the stream; - * see {Character IO}[rdoc-ref:IO@Character+IO]. + * :include: stringio/each_char.rdoc * - * With no block given, returns an enumerator. + * Related: StringIO#each_byte, StringIO#each_codepoint, StringIO#each_line. */ static VALUE strio_each_char(VALUE self) @@ -1074,10 +1192,9 @@ strio_each_char(VALUE self) * call-seq: * each_codepoint {|codepoint| ... } -> self * - * With a block given, calls the block with each remaining codepoint in the stream; - * see {Codepoint IO}[rdoc-ref:IO@Codepoint+IO]. + * :include: stringio/each_codepoint.rdoc * - * With no block given, returns an enumerator. + * Related: StringIO#each_byte, StringIO#each_char, StringIO#each_line. */ static VALUE strio_each_codepoint(VALUE self) @@ -1154,41 +1271,41 @@ prepare_getline_args(struct StringIO *ptr, struct getline_arg *arg, int argc, VA break; case 1: - if (!NIL_P(rs) && !RB_TYPE_P(rs, T_STRING)) { - VALUE tmp = rb_check_string_type(rs); + if (!NIL_P(rs) && !RB_TYPE_P(rs, T_STRING)) { + VALUE tmp = rb_check_string_type(rs); if (NIL_P(tmp)) { - limit = NUM2LONG(rs); - rs = rb_rs; + limit = NUM2LONG(rs); + rs = rb_rs; } else { - rs = tmp; + rs = tmp; } } break; case 2: - if (!NIL_P(rs)) StringValue(rs); + if (!NIL_P(rs)) StringValue(rs); if (!NIL_P(lim)) limit = NUM2LONG(lim); break; } - if (!NIL_P(rs)) { - rb_encoding *enc_rs, *enc_io; - enc_rs = rb_enc_get(rs); - enc_io = get_enc(ptr); - if (enc_rs != enc_io && - (rb_enc_str_coderange(rs) != ENC_CODERANGE_7BIT || - (RSTRING_LEN(rs) > 0 && !rb_enc_asciicompat(enc_io)))) { - if (rs == rb_rs) { - rs = rb_enc_str_new(0, 0, enc_io); - rb_str_buf_cat_ascii(rs, "\n"); - rs = rs; - } - else { - rb_raise(rb_eArgError, "encoding mismatch: %s IO with %s RS", - rb_enc_name(enc_io), - rb_enc_name(enc_rs)); - } - } + if (!NIL_P(ptr->string) && !NIL_P(rs)) { + rb_encoding *enc_rs, *enc_io; + enc_rs = rb_enc_get(rs); + enc_io = get_enc(ptr); + if (enc_rs != enc_io && + (rb_enc_str_coderange(rs) != ENC_CODERANGE_7BIT || + (RSTRING_LEN(rs) > 0 && !rb_enc_asciicompat(enc_io)))) { + if (rs == rb_rs) { + rs = rb_enc_str_new(0, 0, enc_io); + rb_str_buf_cat_ascii(rs, "\n"); + rs = rs; + } + else { + rb_raise(rb_eArgError, "encoding mismatch: %s IO with %s RS", + rb_enc_name(enc_io), + rb_enc_name(enc_rs)); + } + } } arg->rs = rs; arg->limit = limit; @@ -1200,9 +1317,9 @@ prepare_getline_args(struct StringIO *ptr, struct getline_arg *arg, int argc, VA keywords[0] = rb_intern_const("chomp"); } rb_get_kwargs(opts, keywords, 0, 1, &vchomp); - if (respect_chomp) { + if (respect_chomp) { arg->chomp = (vchomp != Qundef) && RTEST(vchomp); - } + } } return arg; } @@ -1226,7 +1343,7 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr) long w = 0; rb_encoding *enc = get_enc(ptr); - if (ptr->pos >= (n = RSTRING_LEN(ptr->string))) { + if (NIL_P(ptr->string) || ptr->pos >= (n = RSTRING_LEN(ptr->string))) { return Qnil; } s = RSTRING_PTR(ptr->string); @@ -1242,7 +1359,7 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr) str = strio_substr(ptr, ptr->pos, e - s - w, enc); } else if ((n = RSTRING_LEN(str)) == 0) { - const char *paragraph_end = NULL; + const char *paragraph_end = NULL; p = s; while (p[(p + 1 < e) && (*p == '\r') && 0] == '\n') { p += *p == '\r'; @@ -1252,18 +1369,18 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr) } s = p; while ((p = memchr(p, '\n', e - p)) && (p != e)) { - p++; - if (!((p < e && *p == '\n') || - (p + 1 < e && *p == '\r' && *(p+1) == '\n'))) { - continue; - } - paragraph_end = p - ((*(p-2) == '\r') ? 2 : 1); - while ((p < e && *p == '\n') || - (p + 1 < e && *p == '\r' && *(p+1) == '\n')) { - p += (*p == '\r') ? 2 : 1; - } - e = p; - break; + p++; + if (!((p < e && *p == '\n') || + (p + 1 < e && *p == '\r' && *(p+1) == '\n'))) { + continue; + } + paragraph_end = p - ((*(p-2) == '\r') ? 2 : 1); + while ((p < e && *p == '\n') || + (p + 1 < e && *p == '\r' && *(p+1) == '\n')) { + p += (*p == '\r') ? 2 : 1; + } + e = p; + break; } if (arg->chomp && paragraph_end) { w = e - paragraph_end; @@ -1311,9 +1428,8 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr) * gets(limit, chomp: false) -> string or nil * gets(sep, limit, chomp: false) -> string or nil * - * Reads and returns a line from the stream; - * assigns the return value to <tt>$_</tt>; - * see {Line IO}[rdoc-ref:IO@Line+IO]. + * :include: stringio/gets.rdoc + * */ static VALUE strio_gets(int argc, VALUE *argv, VALUE self) @@ -1323,6 +1439,7 @@ strio_gets(int argc, VALUE *argv, VALUE self) VALUE str; if (prepare_getline_args(ptr, &arg, argc, argv)->limit == 0) { + if (NIL_P(ptr->string)) return Qnil; return rb_enc_str_new(0, 0, get_enc(ptr)); } @@ -1349,15 +1466,15 @@ strio_readline(int argc, VALUE *argv, VALUE self) } /* + * :markup: markdown + * * call-seq: * each_line(sep = $/, chomp: false) {|line| ... } -> self * each_line(limit, chomp: false) {|line| ... } -> self * each_line(sep, limit, chomp: false) {|line| ... } -> self * - * Calls the block with each remaining line read from the stream; - * does nothing if already at end-of-file; - * returns +self+. - * See {Line IO}[rdoc-ref:IO@Line+IO]. + * :include: stringio/each_line.md + * */ static VALUE strio_each(int argc, VALUE *argv, VALUE self) @@ -1437,6 +1554,7 @@ strio_write(VALUE self, VALUE str) if (!RB_TYPE_P(str, T_STRING)) str = rb_obj_as_string(str); enc = get_enc(ptr); + if (!enc) return 0; enc2 = rb_enc_get(str); if (enc != enc2 && enc != ascii8bit && enc != (usascii = rb_usascii_encoding())) { VALUE converted = rb_str_conv_enc(str, enc2, enc); @@ -1497,9 +1615,10 @@ strio_write(VALUE self, VALUE str) /* * call-seq: - * strio.putc(obj) -> obj + * putc(object) -> object + * + * :include: stringio/putc.rdoc * - * See IO#putc. */ static VALUE strio_putc(VALUE self, VALUE ch) @@ -1509,10 +1628,12 @@ strio_putc(VALUE self, VALUE ch) check_modifiable(ptr); if (RB_TYPE_P(ch, T_STRING)) { + if (NIL_P(ptr->string)) return ch; str = rb_str_substr(ch, 0, 1); } else { char c = NUM2CHR(ch); + if (NIL_P(ptr->string)) return ch; str = rb_str_new(&c, 1); } strio_write(self, str); @@ -1529,9 +1650,10 @@ strio_putc(VALUE self, VALUE ch) /* * call-seq: - * strio.read([length [, outbuf]]) -> string, outbuf, or nil + * read(maxlen = nil, out_string = nil) → new_string, out_string, or nil + * + * :include: stringio/read.rdoc * - * See IO#read. */ static VALUE strio_read(int argc, VALUE *argv, VALUE self) @@ -1555,15 +1677,16 @@ strio_read(int argc, VALUE *argv, VALUE self) if (len < 0) { rb_raise(rb_eArgError, "negative length %ld given", len); } - if (len > 0 && ptr->pos >= RSTRING_LEN(ptr->string)) { + if (eos_p(ptr)) { if (!NIL_P(str)) rb_str_resize(str, 0); - return Qnil; + return len > 0 ? Qnil : rb_str_new(0, 0); } binary = 1; break; } /* fall through */ case 0: + if (NIL_P(ptr->string)) return Qnil; len = RSTRING_LEN(ptr->string); if (len <= ptr->pos) { rb_encoding *enc = get_enc(ptr); @@ -1581,7 +1704,7 @@ strio_read(int argc, VALUE *argv, VALUE self) } break; default: - rb_error_arity(argc, 0, 2); + rb_error_arity(argc, 0, 2); } if (NIL_P(str)) { rb_encoding *enc = binary ? rb_ascii8bit_encoding() : get_enc(ptr); @@ -1592,10 +1715,9 @@ strio_read(int argc, VALUE *argv, VALUE self) if (len > rest) len = rest; rb_str_resize(str, len); MEMCPY(RSTRING_PTR(str), RSTRING_PTR(ptr->string) + ptr->pos, char, len); - if (binary) - rb_enc_associate(str, rb_ascii8bit_encoding()); - else + if (!binary) { rb_enc_copy(str, ptr->string); + } } ptr->pos += RSTRING_LEN(str); return str; @@ -1603,10 +1725,10 @@ strio_read(int argc, VALUE *argv, VALUE self) /* * call-seq: - * pread(maxlen, offset) -> string - * pread(maxlen, offset, out_string) -> string + * pread(maxlen, offset, out_string = nil) -> new_string or out_string + * + * :include: stringio/pread.rdoc * - * See IO#pread. */ static VALUE strio_pread(int argc, VALUE *argv, VALUE self) @@ -1617,28 +1739,28 @@ strio_pread(int argc, VALUE *argv, VALUE self) long offset = NUM2LONG(rb_offset); if (len < 0) { - rb_raise(rb_eArgError, "negative string size (or size too big): %" PRIsVALUE, rb_len); + rb_raise(rb_eArgError, "negative string size (or size too big): %" PRIsVALUE, rb_len); } if (len == 0) { - if (NIL_P(rb_buf)) { - return rb_str_new("", 0); - } - return rb_buf; + if (NIL_P(rb_buf)) { + return rb_str_new("", 0); + } + return rb_buf; } if (offset < 0) { - rb_syserr_fail_str(EINVAL, rb_sprintf("pread: Invalid offset argument: %" PRIsVALUE, rb_offset)); + rb_syserr_fail_str(EINVAL, rb_sprintf("pread: Invalid offset argument: %" PRIsVALUE, rb_offset)); } struct StringIO *ptr = readable(self); - if (offset >= RSTRING_LEN(ptr->string)) { - rb_eof_error(); + if (outside_p(ptr, offset)) { + rb_eof_error(); } if (NIL_P(rb_buf)) { - return strio_substr(ptr, offset, len, rb_ascii8bit_encoding()); + return strio_substr(ptr, offset, len, rb_ascii8bit_encoding()); } long rest = RSTRING_LEN(ptr->string) - offset; @@ -1698,8 +1820,14 @@ strio_read_nonblock(int argc, VALUE *argv, VALUE self) return val; } +/* + * See IO#write + */ #define strio_syswrite rb_io_write +/* + * See IO#write_nonblock + */ static VALUE strio_syswrite_nonblock(int argc, VALUE *argv, VALUE self) { @@ -1717,17 +1845,17 @@ strio_syswrite_nonblock(int argc, VALUE *argv, VALUE self) /* * call-seq: - * strio.length -> integer - * strio.size -> integer + * size -> integer + * + * :include: stringio/size.rdoc * - * Returns the size of the buffer string. */ static VALUE strio_size(VALUE self) { VALUE string = StringIO(self)->string; if (NIL_P(string)) { - rb_raise(rb_eIOError, "not opened"); + return INT2FIX(0); } return ULONG2NUM(RSTRING_LEN(string)); } @@ -1744,10 +1872,12 @@ strio_truncate(VALUE self, VALUE len) { VALUE string = writable(self)->string; long l = NUM2LONG(len); - long plen = RSTRING_LEN(string); + long plen; if (l < 0) { error_inval("negative length"); } + if (NIL_P(string)) return 0; + plen = RSTRING_LEN(string); rb_str_resize(string, l); if (plen < l) { MEMZERO(RSTRING_PTR(string) + plen, char, l - plen); @@ -1756,12 +1886,20 @@ strio_truncate(VALUE self, VALUE len) } /* - * call-seq: - * strio.external_encoding => encoding + * call-seq: + * external_encoding -> encoding or nil + * + * Returns an Encoding object that represents the encoding of the string; + * see {Encodings}[rdoc-ref:StringIO@Encodings]: + * + * strio = StringIO.new('foo') + * strio.external_encoding # => #<Encoding:UTF-8> + * + * Returns +nil+ if +self+ has no string and is in write mode: + * + * strio = StringIO.new(nil, 'w+') + * strio.external_encoding # => nil * - * Returns the Encoding object that represents the encoding of the file. - * If the stream is write mode and no encoding is specified, returns - * +nil+. */ static VALUE @@ -1773,10 +1911,9 @@ strio_external_encoding(VALUE self) /* * call-seq: - * strio.internal_encoding => encoding + * internal_encoding -> nil * - * Returns the Encoding of the internal string if conversion is - * specified. Otherwise returns +nil+. + * Returns +nil+; for compatibility with IO. */ static VALUE @@ -1811,20 +1948,30 @@ strio_set_encoding(int argc, VALUE *argv, VALUE self) enc = rb_find_encoding(ext_enc); if (!enc) { rb_io_enc_t convconfig; - int oflags, fmode; + int oflags; + rb_io_mode_t fmode; VALUE vmode = rb_str_append(rb_str_new_cstr("r:"), ext_enc); rb_io_extract_modeenc(&vmode, 0, Qnil, &oflags, &fmode, &convconfig); enc = convconfig.enc2; } } ptr->enc = enc; - if (WRITABLE(self)) { + if (!NIL_P(ptr->string) && WRITABLE(self) && !str_chilled_p(ptr->string)) { rb_enc_associate(ptr->string, enc); } return self; } +/* + * call-seq: + * strio.set_encoding_by_bom => strio or nil + * + * Sets the encoding according to the BOM (Byte Order Mark) in the + * string. + * + * Returns +self+ if the BOM is found, otherwise +nil. + */ static VALUE strio_set_encoding_by_bom(VALUE self) { @@ -1835,16 +1982,9 @@ strio_set_encoding_by_bom(VALUE self) } /* - * \IO streams for strings, with access similar to - * {IO}[rdoc-ref:IO]; - * see {IO}[rdoc-ref:IO]. - * - * === About the Examples - * - * Examples on this page assume that \StringIO has been required: - * - * require 'stringio' + * :markup: markdown * + * :include: stringio/stringio.md */ void Init_stringio(void) @@ -1852,15 +1992,20 @@ Init_stringio(void) #undef rb_intern #ifdef HAVE_RB_EXT_RACTOR_SAFE - rb_ext_ractor_safe(true); + rb_ext_ractor_safe(true); #endif VALUE StringIO = rb_define_class("StringIO", rb_cObject); + /* The version string */ rb_define_const(StringIO, "VERSION", rb_str_new_cstr(STRINGIO_VERSION)); rb_include_module(StringIO, rb_mEnumerable); rb_define_alloc_func(StringIO, strio_s_allocate); + + /* Maximum length that a StringIO instance can hold */ + rb_define_const(StringIO, "MAX_LENGTH", LONG2NUM(LONG_MAX)); + rb_define_singleton_method(StringIO, "new", strio_s_new, -1); rb_define_singleton_method(StringIO, "open", strio_s_open, -1); rb_define_method(StringIO, "initialize", strio_initialize, -1); @@ -1939,7 +2084,9 @@ Init_stringio(void) rb_define_method(StringIO, "set_encoding_by_bom", strio_set_encoding_by_bom, 0); { + /* :stopdoc: */ VALUE mReadable = rb_define_module_under(rb_cIO, "generic_readable"); + /* :startdoc: */ rb_define_method(mReadable, "readchar", strio_readchar, 0); rb_define_method(mReadable, "readbyte", strio_readbyte, 0); rb_define_method(mReadable, "readline", strio_readline, -1); @@ -1949,7 +2096,9 @@ Init_stringio(void) rb_include_module(StringIO, mReadable); } { + /* :stopdoc: */ VALUE mWritable = rb_define_module_under(rb_cIO, "generic_writable"); + /* :startdoc: */ rb_define_method(mWritable, "<<", strio_addstr, 1); rb_define_method(mWritable, "print", strio_print, -1); rb_define_method(mWritable, "printf", strio_printf, -1); diff --git a/ext/stringio/stringio.gemspec b/ext/stringio/stringio.gemspec index 8c950f8ff9..f9a0742049 100644 --- a/ext/stringio/stringio.gemspec +++ b/ext/stringio/stringio.gemspec @@ -28,11 +28,19 @@ Gem::Specification.new do |s| s.extensions = ["ext/stringio/extconf.rb"] s.files += ["ext/stringio/extconf.rb", "ext/stringio/stringio.c"] end + + s.extra_rdoc_files = [ + ".document", ".rdoc_options", "COPYING", "LICENSE.txt", + "NEWS.md", "README.md", "docs/io.rb", "ext/stringio/.document", + ] + s.homepage = "https://github.com/ruby/stringio" s.licenses = ["Ruby", "BSD-2-Clause"] s.required_ruby_version = ">= 2.7" s.summary = "Pseudo IO on String" + s.metadata["changelog_uri"] = "#{s.homepage}/releases/tag/v#{s.version}" + # s.cert_chain = %w[certs/nobu.pem] # s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/ end diff --git a/ext/strscan/depend b/ext/strscan/depend index 8d985b59e8..b40a025230 100644 --- a/ext/strscan/depend +++ b/ext/strscan/depend @@ -138,6 +138,7 @@ strscan.o: $(hdrdir)/ruby/internal/intern/re.h strscan.o: $(hdrdir)/ruby/internal/intern/ruby.h strscan.o: $(hdrdir)/ruby/internal/intern/select.h strscan.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +strscan.o: $(hdrdir)/ruby/internal/intern/set.h strscan.o: $(hdrdir)/ruby/internal/intern/signal.h strscan.o: $(hdrdir)/ruby/internal/intern/sprintf.h strscan.o: $(hdrdir)/ruby/internal/intern/string.h @@ -157,6 +158,7 @@ strscan.o: $(hdrdir)/ruby/internal/special_consts.h strscan.o: $(hdrdir)/ruby/internal/static_assert.h strscan.o: $(hdrdir)/ruby/internal/stdalign.h strscan.o: $(hdrdir)/ruby/internal/stdbool.h +strscan.o: $(hdrdir)/ruby/internal/stdckdint.h strscan.o: $(hdrdir)/ruby/internal/symbol.h strscan.o: $(hdrdir)/ruby/internal/value.h strscan.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/strscan/extconf.rb b/ext/strscan/extconf.rb index bd65606a4e..d2e9343cbc 100644 --- a/ext/strscan/extconf.rb +++ b/ext/strscan/extconf.rb @@ -2,8 +2,9 @@ require 'mkmf' if RUBY_ENGINE == 'ruby' $INCFLAGS << " -I$(top_srcdir)" if $extmk - have_func("onig_region_memsize", "ruby.h") - have_func("rb_reg_onig_match", "ruby.h") + have_func("onig_region_memsize(NULL)") + have_func("rb_reg_onig_match", "ruby/re.h") + have_func("rb_deprecate_constant") create_makefile 'strscan' else File.write('Makefile', dummy_makefile("").join) diff --git a/ext/strscan/lib/strscan/strscan.rb b/ext/strscan/lib/strscan/strscan.rb new file mode 100644 index 0000000000..46acc7ea82 --- /dev/null +++ b/ext/strscan/lib/strscan/strscan.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class StringScanner + # call-seq: + # scan_integer(base: 10) + # + # If `base` isn't provided or is `10`, then it is equivalent to calling `#scan` with a `[+-]?\d+` pattern, + # and returns an Integer or nil. + # + # If `base` is `16`, then it is equivalent to calling `#scan` with a `[+-]?(0x)?[0-9a-fA-F]+` pattern, + # and returns an Integer or nil. + # + # The scanned string must be encoded with an ASCII compatible encoding, otherwise + # Encoding::CompatibilityError will be raised. + def scan_integer(base: 10) + case base + when 10 + scan_base10_integer + when 16 + scan_base16_integer + else + raise ArgumentError, "Unsupported integer base: #{base.inspect}, expected 10 or 16" + end + end +end diff --git a/ext/strscan/strscan.c b/ext/strscan/strscan.c index 4598d13c90..935fce19df 100644 --- a/ext/strscan/strscan.c +++ b/ext/strscan/strscan.c @@ -22,7 +22,15 @@ extern size_t onig_region_memsize(const struct re_registers *regs); #include <stdbool.h> -#define STRSCAN_VERSION "3.0.9" +#define STRSCAN_VERSION "3.1.7.dev" + + +#ifdef HAVE_RB_DEPRECATE_CONSTANT +/* In ruby 3.0, defined but exposed in external headers */ +extern void rb_deprecate_constant(VALUE mod, const char *name); +#else +# define rb_deprecate_constant(mod, name) ((void)0) +#endif /* ======================================================================= Data Type Definitions @@ -30,7 +38,8 @@ extern size_t onig_region_memsize(const struct re_registers *regs); static VALUE StringScanner; static VALUE ScanError; -static ID id_byteslice; + +static int usascii_encindex, utf8_encindex, binary_encindex; struct strscanner { @@ -56,8 +65,13 @@ struct strscanner }; #define MATCHED_P(s) ((s)->flags & FLAG_MATCHED) -#define MATCHED(s) (s)->flags |= FLAG_MATCHED -#define CLEAR_MATCH_STATUS(s) (s)->flags &= ~FLAG_MATCHED +#define MATCHED(s) ((s)->flags |= FLAG_MATCHED) +#define CLEAR_MATCHED(s) ((s)->flags &= ~FLAG_MATCHED) +#define CLEAR_NAMED_CAPTURES(s) ((s)->regex = Qnil) +#define CLEAR_MATCH_STATUS(s) do {\ + CLEAR_MATCHED(s);\ + CLEAR_NAMED_CAPTURES(s);\ +} while (0) #define S_PBEG(s) (RSTRING_PTR((s)->str)) #define S_LEN(s) (RSTRING_LEN((s)->str)) @@ -90,7 +104,6 @@ static VALUE strscan_init_copy _((VALUE vself, VALUE vorig)); static VALUE strscan_s_mustc _((VALUE self)); static VALUE strscan_terminate _((VALUE self)); -static VALUE strscan_clear _((VALUE self)); static VALUE strscan_get_string _((VALUE self)); static VALUE strscan_set_string _((VALUE self, VALUE str)); static VALUE strscan_concat _((VALUE self, VALUE str)); @@ -112,13 +125,11 @@ static VALUE strscan_search_full _((VALUE self, VALUE re, static void adjust_registers_to_matched _((struct strscanner *p)); static VALUE strscan_getch _((VALUE self)); static VALUE strscan_get_byte _((VALUE self)); -static VALUE strscan_getbyte _((VALUE self)); static VALUE strscan_peek _((VALUE self, VALUE len)); -static VALUE strscan_peep _((VALUE self, VALUE len)); +static VALUE strscan_scan_base10_integer _((VALUE self)); static VALUE strscan_unscan _((VALUE self)); static VALUE strscan_bol_p _((VALUE self)); static VALUE strscan_eos_p _((VALUE self)); -static VALUE strscan_empty_p _((VALUE self)); static VALUE strscan_rest_p _((VALUE self)); static VALUE strscan_matched_p _((VALUE self)); static VALUE strscan_matched _((VALUE self)); @@ -201,7 +212,7 @@ strscan_memsize(const void *ptr) static const rb_data_type_t strscanner_type = { "StringScanner", {strscan_mark, strscan_free, strscan_memsize}, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED }; static VALUE @@ -213,21 +224,32 @@ strscan_s_allocate(VALUE klass) CLEAR_MATCH_STATUS(p); onig_region_init(&(p->regs)); p->str = Qnil; - p->regex = Qnil; return obj; } /* - * call-seq: - * StringScanner.new(string, fixed_anchor: false) - * StringScanner.new(string, dup = false) + * :markup: markdown + * :include: strscan/link_refs.txt * - * Creates a new StringScanner object to scan over the given +string+. - * - * If +fixed_anchor+ is +true+, +\A+ always matches the beginning of - * the string. Otherwise, +\A+ always matches the current position. + * call-seq: + * StringScanner.new(string, fixed_anchor: false) -> string_scanner + * + * Returns a new `StringScanner` object whose [stored string][1] + * is the given `string`; + * sets the [fixed-anchor property][10]: + * + * ```rb + * scanner = StringScanner.new('foobarbaz') + * scanner.string # => "foobarbaz" + * scanner.fixed_anchor? # => false + * put_situation(scanner) + * # Situation: + * # pos: 0 + * # charpos: 0 + * # rest: "foobarbaz" + * # rest_size: 9 + * ``` * - * +dup+ argument is obsolete and not used now. */ static VALUE strscan_initialize(int argc, VALUE *argv, VALUE self) @@ -254,7 +276,7 @@ strscan_initialize(int argc, VALUE *argv, VALUE self) p->fixed_anchor_p = false; } StringValue(str); - p->str = str; + RB_OBJ_WRITE(self, &p->str, str); return self; } @@ -266,11 +288,14 @@ check_strscan(VALUE obj) } /* + * :markup: markdown + * :include: strscan/link_refs.txt + * * call-seq: - * dup - * clone + * dup -> shallow_copy * - * Duplicates a StringScanner object. + * Returns a shallow copy of `self`; + * the [stored string][1] in the copy is the same string as in `self`. */ static VALUE strscan_init_copy(VALUE vself, VALUE vorig) @@ -281,7 +306,7 @@ strscan_init_copy(VALUE vself, VALUE vorig) orig = check_strscan(vorig); if (self != orig) { self->flags = orig->flags; - self->str = orig->str; + RB_OBJ_WRITE(vself, &self->str, orig->str); self->prev = orig->prev; self->curr = orig->curr; if (rb_reg_region_copy(&self->regs, &orig->regs)) @@ -297,10 +322,13 @@ strscan_init_copy(VALUE vself, VALUE vorig) ======================================================================= */ /* - * call-seq: StringScanner.must_C_version + * call-seq: + * StringScanner.must_C_version -> self * - * This method is defined for backward compatibility. + * Returns +self+; defined for backward compatibility. */ + + /* :nodoc: */ static VALUE strscan_s_mustc(VALUE self) { @@ -308,7 +336,30 @@ strscan_s_mustc(VALUE self) } /* - * Reset the scan pointer (index 0) and clear matching data. + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * reset -> self + * + * Sets both [byte position][2] and [character position][7] to zero, + * and clears [match values][9]; + * returns +self+: + * + * ```rb + * scanner = StringScanner.new('foobarbaz') + * scanner.exist?(/bar/) # => 6 + * scanner.reset # => #<StringScanner 0/9 @ "fooba..."> + * put_situation(scanner) + * # Situation: + * # pos: 0 + * # charpos: 0 + * # rest: "foobarbaz" + * # rest_size: 9 + * # => nil + * match_values_cleared?(scanner) # => true + * ``` + * */ static VALUE strscan_reset(VALUE self) @@ -322,11 +373,9 @@ strscan_reset(VALUE self) } /* - * call-seq: - * terminate - * clear - * - * Sets the scan pointer to the end of the string and clear matching data. + * :markup: markdown + * :include: strscan/link_refs.txt + * :include: strscan/methods/terminate.md */ static VALUE strscan_terminate(VALUE self) @@ -340,18 +389,21 @@ strscan_terminate(VALUE self) } /* - * Equivalent to #terminate. - * This method is obsolete; use #terminate instead. - */ -static VALUE -strscan_clear(VALUE self) -{ - rb_warning("StringScanner#clear is obsolete; use #terminate instead"); - return strscan_terminate(self); -} - -/* - * Returns the string being scanned. + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * string -> stored_string + * + * Returns the [stored string][1]: + * + * ```rb + * scanner = StringScanner.new('foobar') + * scanner.string # => "foobar" + * scanner.concat('baz') + * scanner.string # => "foobarbaz" + * ``` + * */ static VALUE strscan_get_string(VALUE self) @@ -363,10 +415,39 @@ strscan_get_string(VALUE self) } /* - * call-seq: string=(str) + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * string = other_string -> other_string + * + * Replaces the [stored string][1] with the given `other_string`: + * + * - Sets both [positions][11] to zero. + * - Clears [match values][9]. + * - Returns `other_string`. + * + * ```rb + * scanner = StringScanner.new('foobar') + * scanner.scan(/foo/) + * put_situation(scanner) + * # Situation: + * # pos: 3 + * # charpos: 3 + * # rest: "bar" + * # rest_size: 3 + * match_values_cleared?(scanner) # => false + * + * scanner.string = 'baz' # => "baz" + * put_situation(scanner) + * # Situation: + * # pos: 0 + * # charpos: 0 + * # rest: "baz" + * # rest_size: 3 + * match_values_cleared?(scanner) # => true + * ``` * - * Changes the string being scanned to +str+ and resets the scanner. - * Returns +str+. */ static VALUE strscan_set_string(VALUE self, VALUE str) @@ -374,25 +455,40 @@ strscan_set_string(VALUE self, VALUE str) struct strscanner *p = check_strscan(self); StringValue(str); - p->str = str; + RB_OBJ_WRITE(self, &p->str, str); p->curr = 0; CLEAR_MATCH_STATUS(p); return str; } /* - * call-seq: - * concat(str) - * <<(str) + * :markup: markdown + * :include: strscan/link_refs.txt * - * Appends +str+ to the string being scanned. - * This method does not affect scan pointer. + * call-seq: + * concat(more_string) -> self + * + * - Appends the given `more_string` + * to the [stored string][1]. + * - Returns `self`. + * - Does not affect the [positions][11] + * or [match values][9]. + * + * + * ```rb + * scanner = StringScanner.new('foo') + * scanner.string # => "foo" + * scanner.terminate + * scanner.concat('barbaz') # => #<StringScanner 3/9 "foo" @ "barba..."> + * scanner.string # => "foobarbaz" + * put_situation(scanner) + * # Situation: + * # pos: 3 + * # charpos: 3 + * # rest: "barbaz" + * # rest_size: 6 + * ``` * - * s = StringScanner.new("Fri Dec 12 1975 14:39") - * s.scan(/Fri /) - * s << " +1000 GMT" - * s.string # -> "Fri Dec 12 1975 14:39 +1000 GMT" - * s.scan(/Dec/) # -> "Dec" */ static VALUE strscan_concat(VALUE self, VALUE str) @@ -406,18 +502,9 @@ strscan_concat(VALUE self, VALUE str) } /* - * Returns the byte position of the scan pointer. In the 'reset' position, this - * value is zero. In the 'terminated' position (i.e. the string is exhausted), - * this value is the bytesize of the string. - * - * In short, it's a 0-based index into bytes of the string. - * - * s = StringScanner.new('test string') - * s.pos # -> 0 - * s.scan_until /str/ # -> "test str" - * s.pos # -> 8 - * s.terminate # -> #<StringScanner fin> - * s.pos # -> 11 + * :markup: markdown + * :include: strscan/link_refs.txt + * :include: strscan/methods/get_pos.md */ static VALUE strscan_get_pos(VALUE self) @@ -425,21 +512,13 @@ strscan_get_pos(VALUE self) struct strscanner *p; GET_SCANNER(self, p); - return INT2FIX(p->curr); + return LONG2NUM(p->curr); } /* - * Returns the character position of the scan pointer. In the 'reset' position, this - * value is zero. In the 'terminated' position (i.e. the string is exhausted), - * this value is the size of the string. - * - * In short, it's a 0-based index into the string. - * - * s = StringScanner.new("abc\u00e4def\u00f6ghi") - * s.charpos # -> 0 - * s.scan_until(/\u00e4/) # -> "abc\u00E4" - * s.pos # -> 5 - * s.charpos # -> 4 + * :markup: markdown + * :include: strscan/link_refs.txt + * :include: strscan/methods/get_charpos.md */ static VALUE strscan_get_charpos(VALUE self) @@ -452,13 +531,9 @@ strscan_get_charpos(VALUE self) } /* - * call-seq: pos=(n) - * - * Sets the byte position of the scan pointer. - * - * s = StringScanner.new('test string') - * s.pos = 7 # -> 7 - * s.rest # -> "ring" + * :markup: markdown + * :include: strscan/link_refs.txt + * :include: strscan/methods/set_pos.md */ static VALUE strscan_set_pos(VALUE self, VALUE v) @@ -467,7 +542,7 @@ strscan_set_pos(VALUE self, VALUE v) long i; GET_SCANNER(self, p); - i = NUM2INT(v); + i = NUM2LONG(v); if (i < 0) i += S_LEN(p); if (i < 0) rb_raise(rb_eRangeError, "index out of range"); if (i > S_LEN(p)) rb_raise(rb_eRangeError, "index out of range"); @@ -488,19 +563,20 @@ match_target(struct strscanner *p) } static inline void -set_registers(struct strscanner *p, size_t length) +set_registers(struct strscanner *p, size_t pos, size_t length) { const int at = 0; OnigRegion *regs = &(p->regs); onig_region_clear(regs); if (onig_region_set(regs, at, 0, 0)) return; if (p->fixed_anchor_p) { - regs->beg[at] = p->curr; - regs->end[at] = p->curr + length; + regs->beg[at] = pos + p->curr; + regs->end[at] = pos + p->curr + length; } else { - regs->end[at] = length; + regs->beg[at] = pos; + regs->end[at] = pos + length; } } @@ -546,12 +622,13 @@ rb_reg_onig_match(VALUE re, VALUE str, OnigPosition (*match)(regex_t *reg, VALUE str, struct re_registers *regs, void *args), void *args, struct re_registers *regs) { + OnigPosition result; regex_t *reg = rb_reg_prepare_re(re, str); bool tmpreg = reg != RREGEXP_PTR(re); if (!tmpreg) RREGEXP(re)->usecnt++; - OnigPosition result = match(reg, str, regs, args); + result = match(reg, str, regs, args); if (!tmpreg) RREGEXP(re)->usecnt--; if (tmpreg) { @@ -601,19 +678,19 @@ strscan_search(regex_t *reg, VALUE str, struct re_registers *regs, void *args_pt ONIG_OPTION_NONE); } +static void +strscan_enc_check(VALUE str1, VALUE str2) +{ + if (RB_ENCODING_GET(str1) != RB_ENCODING_GET(str2)) { + rb_enc_check(str1, str2); + } +} + static VALUE strscan_do_scan(VALUE self, VALUE pattern, int succptr, int getstr, int headonly) { struct strscanner *p; - if (headonly) { - if (!RB_TYPE_P(pattern, T_REGEXP)) { - StringValue(pattern); - } - } - else { - Check_Type(pattern, T_REGEXP); - } GET_SCANNER(self, p); CLEAR_MATCH_STATUS(p); @@ -622,26 +699,42 @@ strscan_do_scan(VALUE self, VALUE pattern, int succptr, int getstr, int headonly } if (RB_TYPE_P(pattern, T_REGEXP)) { - p->regex = pattern; - OnigPosition ret = rb_reg_onig_match(pattern, - p->str, - headonly ? strscan_match : strscan_search, - (void *)p, - &(p->regs)); + OnigPosition ret; + RB_OBJ_WRITE(self, &p->regex, pattern); + ret = rb_reg_onig_match(p->regex, + p->str, + headonly ? strscan_match : strscan_search, + (void *)p, + &(p->regs)); if (ret == ONIG_MISMATCH) { return Qnil; } } else { - rb_enc_check(p->str, pattern); + StringValue(pattern); if (S_RESTLEN(p) < RSTRING_LEN(pattern)) { + strscan_enc_check(p->str, pattern); return Qnil; } - if (memcmp(CURPTR(p), RSTRING_PTR(pattern), RSTRING_LEN(pattern)) != 0) { - return Qnil; + + if (headonly) { + strscan_enc_check(p->str, pattern); + + if (memcmp(CURPTR(p), RSTRING_PTR(pattern), RSTRING_LEN(pattern)) != 0) { + return Qnil; + } + set_registers(p, 0, RSTRING_LEN(pattern)); + } + else { + rb_encoding *enc = rb_enc_check(p->str, pattern); + long pos = rb_memsearch(RSTRING_PTR(pattern), RSTRING_LEN(pattern), + CURPTR(p), S_RESTLEN(p), enc); + if (pos == -1) { + return Qnil; + } + set_registers(p, pos, RSTRING_LEN(pattern)); } - set_registers(p, RSTRING_LEN(pattern)); } MATCHED(p); @@ -662,20 +755,9 @@ strscan_do_scan(VALUE self, VALUE pattern, int succptr, int getstr, int headonly } /* - * call-seq: scan(pattern) => String - * - * Tries to match with +pattern+ at the current position. If there's a match, - * the scanner advances the "scan pointer" and returns the matched string. - * Otherwise, the scanner returns +nil+. - * - * s = StringScanner.new('test string') - * p s.scan(/\w+/) # -> "test" - * p s.scan(/\w+/) # -> nil - * p s.scan(/\s+/) # -> " " - * p s.scan("str") # -> "str" - * p s.scan(/\w+/) # -> "ing" - * p s.scan(/./) # -> nil - * + * :markup: markdown + * :include: strscan/link_refs.txt + * :include: strscan/methods/scan.md */ static VALUE strscan_scan(VALUE self, VALUE re) @@ -684,16 +766,60 @@ strscan_scan(VALUE self, VALUE re) } /* - * call-seq: match?(pattern) + * :markup: markdown + * :include: strscan/link_refs.txt * - * Tests whether the given +pattern+ is matched from the current scan pointer. - * Returns the length of the match, or +nil+. The scan pointer is not advanced. + * call-seq: + * match?(pattern) -> updated_position or nil + * + * Attempts to [match][17] the given `pattern` + * at the beginning of the [target substring][3]; + * does not modify the [positions][11]. + * + * If the match succeeds: + * + * - Sets [match values][9]. + * - Returns the size in bytes of the matched substring. + * + * + * ```rb + * scanner = StringScanner.new('foobarbaz') + * scanner.pos = 3 + * scanner.match?(/bar/) => 3 + * put_match_values(scanner) + * # Basic match values: + * # matched?: true + * # matched_size: 3 + * # pre_match: "foo" + * # matched : "bar" + * # post_match: "baz" + * # Captured match values: + * # size: 1 + * # captures: [] + * # named_captures: {} + * # values_at: ["bar", nil] + * # []: + * # [0]: "bar" + * # [1]: nil + * put_situation(scanner) + * # Situation: + * # pos: 3 + * # charpos: 3 + * # rest: "barbaz" + * # rest_size: 6 + * ``` + * + * If the match fails: + * + * - Clears match values. + * - Returns `nil`. + * - Does not increment positions. + * + * ```rb + * scanner.match?(/nope/) # => nil + * match_values_cleared?(scanner) # => true + * ``` * - * s = StringScanner.new('test string') - * p s.match?(/\w+/) # -> 4 - * p s.match?(/\w+/) # -> 4 - * p s.match?("test") # -> 4 - * p s.match?(/\s+/) # -> nil */ static VALUE strscan_match_p(VALUE self, VALUE re) @@ -702,22 +828,9 @@ strscan_match_p(VALUE self, VALUE re) } /* - * call-seq: skip(pattern) - * - * Attempts to skip over the given +pattern+ beginning with the scan pointer. - * If it matches, the scan pointer is advanced to the end of the match, and the - * length of the match is returned. Otherwise, +nil+ is returned. - * - * It's similar to #scan, but without returning the matched string. - * - * s = StringScanner.new('test string') - * p s.skip(/\w+/) # -> 4 - * p s.skip(/\w+/) # -> nil - * p s.skip(/\s+/) # -> 1 - * p s.skip("st") # -> 2 - * p s.skip(/\w+/) # -> 4 - * p s.skip(/./) # -> nil - * + * :markup: markdown + * :include: strscan/link_refs.txt + * :include: strscan/methods/skip.md */ static VALUE strscan_skip(VALUE self, VALUE re) @@ -726,19 +839,59 @@ strscan_skip(VALUE self, VALUE re) } /* - * call-seq: check(pattern) + * :markup: markdown + * :include: strscan/link_refs.txt * - * This returns the value that #scan would return, without advancing the scan - * pointer. The match register is affected, though. - * - * s = StringScanner.new("Fri Dec 12 1975 14:39") - * s.check /Fri/ # -> "Fri" - * s.pos # -> 0 - * s.matched # -> "Fri" - * s.check /12/ # -> nil - * s.matched # -> nil + * call-seq: + * check(pattern) -> matched_substring or nil + * + * Attempts to [match][17] the given `pattern` + * at the beginning of the [target substring][3]; + * does not modify the [positions][11]. + * + * If the match succeeds: + * + * - Returns the matched substring. + * - Sets all [match values][9]. + * + * ```rb + * scanner = StringScanner.new('foobarbaz') + * scanner.pos = 3 + * scanner.check('bar') # => "bar" + * put_match_values(scanner) + * # Basic match values: + * # matched?: true + * # matched_size: 3 + * # pre_match: "foo" + * # matched : "bar" + * # post_match: "baz" + * # Captured match values: + * # size: 1 + * # captures: [] + * # named_captures: {} + * # values_at: ["bar", nil] + * # []: + * # [0]: "bar" + * # [1]: nil + * # => 0..1 + * put_situation(scanner) + * # Situation: + * # pos: 3 + * # charpos: 3 + * # rest: "barbaz" + * # rest_size: 6 + * ``` + * + * If the match fails: + * + * - Returns `nil`. + * - Clears all [match values][9]. + * + * ```rb + * scanner.check(/nope/) # => nil + * match_values_cleared?(scanner) # => true + * ``` * - * Mnemonic: it "checks" to see whether a #scan will return a value. */ static VALUE strscan_check(VALUE self, VALUE re) @@ -747,15 +900,24 @@ strscan_check(VALUE self, VALUE re) } /* - * call-seq: scan_full(pattern, advance_pointer_p, return_string_p) + * call-seq: + * scan_full(pattern, advance_pointer_p, return_string_p) -> matched_substring or nil + * + * Equivalent to one of the following: + * + * - +advance_pointer_p+ +true+: * - * Tests whether the given +pattern+ is matched from the current scan pointer. - * Advances the scan pointer if +advance_pointer_p+ is true. - * Returns the matched string if +return_string_p+ is true. - * The match register is affected. + * - +return_string_p+ +true+: StringScanner#scan(pattern). + * - +return_string_p+ +false+: StringScanner#skip(pattern). + * + * - +advance_pointer_p+ +false+: + * + * - +return_string_p+ +true+: StringScanner#check(pattern). + * - +return_string_p+ +false+: StringScanner#match?(pattern). * - * "full" means "#scan with full parameters". */ + + /* :nodoc: */ static VALUE strscan_scan_full(VALUE self, VALUE re, VALUE s, VALUE f) { @@ -763,16 +925,9 @@ strscan_scan_full(VALUE self, VALUE re, VALUE s, VALUE f) } /* - * call-seq: scan_until(pattern) - * - * Scans the string _until_ the +pattern+ is matched. Returns the substring up - * to and including the end of the match, advancing the scan pointer to that - * location. If there is no match, +nil+ is returned. - * - * s = StringScanner.new("Fri Dec 12 1975 14:39") - * s.scan_until(/1/) # -> "Fri Dec 1" - * s.pre_match # -> "Fri Dec " - * s.scan_until(/XYZ/) # -> nil + * :markup: markdown + * :include: strscan/link_refs.txt + * :include: strscan/methods/scan_until.md */ static VALUE strscan_scan_until(VALUE self, VALUE re) @@ -781,17 +936,61 @@ strscan_scan_until(VALUE self, VALUE re) } /* - * call-seq: exist?(pattern) + * :markup: markdown + * :include: strscan/link_refs.txt * - * Looks _ahead_ to see if the +pattern+ exists _anywhere_ in the string, - * without advancing the scan pointer. This predicates whether a #scan_until - * will return a value. + * call-seq: + * exist?(pattern) -> byte_offset or nil + * + * Attempts to [match][17] the given `pattern` + * anywhere (at any [position][2]) + * n the [target substring][3]; + * does not modify the [positions][11]. + * + * If the match succeeds: + * + * - Returns a byte offset: + * the distance in bytes between the current [position][2] + * and the end of the matched substring. + * - Sets all [match values][9]. + * + * ```rb + * scanner = StringScanner.new('foobarbazbatbam') + * scanner.pos = 6 + * scanner.exist?(/bat/) # => 6 + * put_match_values(scanner) + * # Basic match values: + * # matched?: true + * # matched_size: 3 + * # pre_match: "foobarbaz" + * # matched : "bat" + * # post_match: "bam" + * # Captured match values: + * # size: 1 + * # captures: [] + * # named_captures: {} + * # values_at: ["bat", nil] + * # []: + * # [0]: "bat" + * # [1]: nil + * put_situation(scanner) + * # Situation: + * # pos: 6 + * # charpos: 6 + * # rest: "bazbatbam" + * # rest_size: 9 + * ``` + * + * If the match fails: + * + * - Returns `nil`. + * - Clears all [match values][9]. + * + * ```rb + * scanner.exist?(/nope/) # => nil + * match_values_cleared?(scanner) # => true + * ``` * - * s = StringScanner.new('test string') - * s.exist? /s/ # -> 3 - * s.scan /test/ # -> "test" - * s.exist? /s/ # -> 2 - * s.exist? /e/ # -> nil */ static VALUE strscan_exist_p(VALUE self, VALUE re) @@ -800,20 +999,9 @@ strscan_exist_p(VALUE self, VALUE re) } /* - * call-seq: skip_until(pattern) - * - * Advances the scan pointer until +pattern+ is matched and consumed. Returns - * the number of bytes advanced, or +nil+ if no match was found. - * - * Look ahead to match +pattern+, and advance the scan pointer to the _end_ - * of the match. Return the number of characters advanced, or +nil+ if the - * match was unsuccessful. - * - * It's similar to #scan_until, but without returning the intervening string. - * - * s = StringScanner.new("Fri Dec 12 1975 14:39") - * s.skip_until /12/ # -> 10 - * s # + * :markup: markdown + * :include: strscan/link_refs.txt + * :include: strscan/methods/skip_until.md */ static VALUE strscan_skip_until(VALUE self, VALUE re) @@ -822,17 +1010,61 @@ strscan_skip_until(VALUE self, VALUE re) } /* - * call-seq: check_until(pattern) + * :markup: markdown + * :include: strscan/link_refs.txt * - * This returns the value that #scan_until would return, without advancing the - * scan pointer. The match register is affected, though. - * - * s = StringScanner.new("Fri Dec 12 1975 14:39") - * s.check_until /12/ # -> "Fri Dec 12" - * s.pos # -> 0 - * s.matched # -> 12 + * call-seq: + * check_until(pattern) -> substring or nil + * + * Attempts to [match][17] the given `pattern` + * anywhere (at any [position][2]) + * in the [target substring][3]; + * does not modify the [positions][11]. + * + * If the match succeeds: + * + * - Sets all [match values][9]. + * - Returns the matched substring, + * which extends from the current [position][2] + * to the end of the matched substring. + * + * ```rb + * scanner = StringScanner.new('foobarbazbatbam') + * scanner.pos = 6 + * scanner.check_until(/bat/) # => "bazbat" + * put_match_values(scanner) + * # Basic match values: + * # matched?: true + * # matched_size: 3 + * # pre_match: "foobarbaz" + * # matched : "bat" + * # post_match: "bam" + * # Captured match values: + * # size: 1 + * # captures: [] + * # named_captures: {} + * # values_at: ["bat", nil] + * # []: + * # [0]: "bat" + * # [1]: nil + * put_situation(scanner) + * # Situation: + * # pos: 6 + * # charpos: 6 + * # rest: "bazbatbam" + * # rest_size: 9 + * ``` + * + * If the match fails: + * + * - Clears all [match values][9]. + * - Returns `nil`. + * + * ```rb + * scanner.check_until(/nope/) # => nil + * match_values_cleared?(scanner) # => true + * ``` * - * Mnemonic: it "checks" to see whether a #scan_until will return a value. */ static VALUE strscan_check_until(VALUE self, VALUE re) @@ -841,14 +1073,24 @@ strscan_check_until(VALUE self, VALUE re) } /* - * call-seq: search_full(pattern, advance_pointer_p, return_string_p) + * call-seq: + * search_full(pattern, advance_pointer_p, return_string_p) -> matched_substring or position_delta or nil + * + * Equivalent to one of the following: + * + * - +advance_pointer_p+ +true+: + * + * - +return_string_p+ +true+: StringScanner#scan_until(pattern). + * - +return_string_p+ +false+: StringScanner#skip_until(pattern). + * + * - +advance_pointer_p+ +false+: + * + * - +return_string_p+ +true+: StringScanner#check_until(pattern). + * - +return_string_p+ +false+: StringScanner#exist?(pattern). * - * Scans the string _until_ the +pattern+ is matched. - * Advances the scan pointer if +advance_pointer_p+, otherwise not. - * Returns the matched string if +return_string_p+ is true, otherwise - * returns the number of bytes advanced. - * This method does affect the match register. */ + + /* :nodoc: */ static VALUE strscan_search_full(VALUE self, VALUE re, VALUE s, VALUE f) { @@ -868,17 +1110,9 @@ adjust_registers_to_matched(struct strscanner *p) } /* - * Scans one character and returns it. - * This method is multibyte character sensitive. - * - * s = StringScanner.new("ab") - * s.getch # => "a" - * s.getch # => "b" - * s.getch # => nil - * - * s = StringScanner.new("\244\242".force_encoding("euc-jp")) - * s.getch # => "\x{A4A2}" # Japanese hira-kana "A" in EUC-JP - * s.getch # => nil + * :markup: markdown + * :include: strscan/link_refs.txt + * :include: strscan/methods/getch.md */ static VALUE strscan_getch(VALUE self) @@ -903,59 +1137,92 @@ strscan_getch(VALUE self) } /* - * Scans one byte and returns it. + * call-seq: + * scan_byte -> integer_byte + * + * Scans one byte and returns it as an integer. * This method is not multibyte character sensitive. * See also: #getch. * - * s = StringScanner.new('ab') - * s.get_byte # => "a" - * s.get_byte # => "b" - * s.get_byte # => nil - * - * s = StringScanner.new("\244\242".force_encoding("euc-jp")) - * s.get_byte # => "\xA4" - * s.get_byte # => "\xA2" - * s.get_byte # => nil */ static VALUE -strscan_get_byte(VALUE self) +strscan_scan_byte(VALUE self) { struct strscanner *p; + VALUE byte; GET_SCANNER(self, p); CLEAR_MATCH_STATUS(p); if (EOS_P(p)) return Qnil; + byte = INT2FIX((unsigned char)*CURPTR(p)); p->prev = p->curr; p->curr++; MATCHED(p); adjust_registers_to_matched(p); - return extract_range(p, - adjust_register_position(p, p->regs.beg[0]), - adjust_register_position(p, p->regs.end[0])); + return byte; +} + +/* + * Peeks at the current byte and returns it as an integer. + * + * s = StringScanner.new('ab') + * s.peek_byte # => 97 + */ +static VALUE +strscan_peek_byte(VALUE self) +{ + struct strscanner *p; + + GET_SCANNER(self, p); + if (EOS_P(p)) + return Qnil; + + return INT2FIX((unsigned char)*CURPTR(p)); } /* - * Equivalent to #get_byte. - * This method is obsolete; use #get_byte instead. + * :markup: markdown + * :include: strscan/link_refs.txt + * :include: strscan/methods/get_byte.md */ static VALUE -strscan_getbyte(VALUE self) +strscan_get_byte(VALUE self) { - rb_warning("StringScanner#getbyte is obsolete; use #get_byte instead"); - return strscan_get_byte(self); + struct strscanner *p; + + GET_SCANNER(self, p); + CLEAR_MATCH_STATUS(p); + if (EOS_P(p)) + return Qnil; + + p->prev = p->curr; + p->curr++; + MATCHED(p); + adjust_registers_to_matched(p); + return extract_range(p, + adjust_register_position(p, p->regs.beg[0]), + adjust_register_position(p, p->regs.end[0])); } /* - * call-seq: peek(len) + * :markup: markdown + * :include: strscan/link_refs.txt * - * Extracts a string corresponding to <tt>string[pos,len]</tt>, without - * advancing the scan pointer. + * call-seq: + * peek(length) -> substring * - * s = StringScanner.new('test string') - * s.peek(7) # => "test st" - * s.peek(7) # => "test st" + * Returns the substring `string[pos, length]`; + * does not update [match values][9] or [positions][11]: + * + * ```rb + * scanner = StringScanner.new('foobarbaz') + * scanner.pos = 3 + * scanner.peek(3) # => "bar" + * scanner.terminate + * scanner.peek(3) # => "" + * ``` * */ static VALUE @@ -974,27 +1241,160 @@ strscan_peek(VALUE self, VALUE vlen) return extract_beg_len(p, p->curr, len); } -/* - * Equivalent to #peek. - * This method is obsolete; use #peek instead. - */ static VALUE -strscan_peep(VALUE self, VALUE vlen) +strscan_parse_integer(struct strscanner *p, int base, long len) { - rb_warning("StringScanner#peep is obsolete; use #peek instead"); - return strscan_peek(self, vlen); + VALUE buffer_v, integer; + + char *buffer = RB_ALLOCV_N(char, buffer_v, len + 1); + + MEMCPY(buffer, CURPTR(p), char, len); + buffer[len] = '\0'; + integer = rb_cstr2inum(buffer, base); + RB_ALLOCV_END(buffer_v); + p->curr += len; + + MATCHED(p); + adjust_registers_to_matched(p); + + return integer; +} + +static inline bool +strscan_ascii_compat_fastpath(VALUE str) { + int encindex = ENCODING_GET_INLINED(str); + // The overwhelming majority of strings are in one of these 3 encodings. + return encindex == utf8_encindex || encindex == binary_encindex || encindex == usascii_encindex; +} + +static inline void +strscan_must_ascii_compat(VALUE str) +{ + // The overwhelming majority of strings are in one of these 3 encodings. + if (RB_LIKELY(strscan_ascii_compat_fastpath(str))) { + return; + } + + rb_must_asciicompat(str); +} + +/* :nodoc: */ +static VALUE +strscan_scan_base10_integer(VALUE self) +{ + char *ptr; + long len = 0, remaining_len; + struct strscanner *p; + + GET_SCANNER(self, p); + CLEAR_MATCH_STATUS(p); + + strscan_must_ascii_compat(p->str); + + ptr = CURPTR(p); + + remaining_len = S_RESTLEN(p); + + if (remaining_len <= 0) { + return Qnil; + } + + if (ptr[len] == '-' || ptr[len] == '+') { + len++; + } + + if (!rb_isdigit(ptr[len])) { + return Qnil; + } + + p->prev = p->curr; + + while (len < remaining_len && rb_isdigit(ptr[len])) { + len++; + } + + return strscan_parse_integer(p, 10, len); +} + +/* :nodoc: */ +static VALUE +strscan_scan_base16_integer(VALUE self) +{ + char *ptr; + long len = 0, remaining_len; + struct strscanner *p; + + GET_SCANNER(self, p); + CLEAR_MATCH_STATUS(p); + + strscan_must_ascii_compat(p->str); + + ptr = CURPTR(p); + + remaining_len = S_RESTLEN(p); + + if (remaining_len <= 0) { + return Qnil; + } + + if (ptr[len] == '-' || ptr[len] == '+') { + len++; + } + + if ((remaining_len >= (len + 3)) && ptr[len] == '0' && ptr[len + 1] == 'x' && rb_isxdigit(ptr[len + 2])) { + len += 2; + } + + if (len >= remaining_len || !rb_isxdigit(ptr[len])) { + return Qnil; + } + + p->prev = p->curr; + + while (len < remaining_len && rb_isxdigit(ptr[len])) { + len++; + } + + return strscan_parse_integer(p, 16, len); } /* - * Sets the scan pointer to the previous position. Only one previous position is - * remembered, and it changes with each scanning operation. + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * unscan -> self + * + * Sets the [position][2] to its value previous to the recent successful + * [match][17] attempt: + * + * ```rb + * scanner = StringScanner.new('foobarbaz') + * scanner.scan(/foo/) + * put_situation(scanner) + * # Situation: + * # pos: 3 + * # charpos: 3 + * # rest: "barbaz" + * # rest_size: 6 + * scanner.unscan + * # => #<StringScanner 0/9 @ "fooba..."> + * put_situation(scanner) + * # Situation: + * # pos: 0 + * # charpos: 0 + * # rest: "foobarbaz" + * # rest_size: 9 + * ``` + * + * Raises an exception if match values are clear: + * + * ```rb + * scanner.scan(/nope/) # => nil + * match_values_cleared?(scanner) # => true + * scanner.unscan # Raises StringScanner::Error. + * ``` * - * s = StringScanner.new('test string') - * s.scan(/\w+/) # => "test" - * s.unscan - * s.scan(/../) # => "te" - * s.scan(/\d/) # => nil - * s.unscan # ScanError: unscan failed: previous match record not exist */ static VALUE strscan_unscan(VALUE self) @@ -1010,16 +1410,37 @@ strscan_unscan(VALUE self) } /* - * Returns +true+ if and only if the scan pointer is at the beginning of the line. - * - * s = StringScanner.new("test\ntest\n") - * s.bol? # => true - * s.scan(/te/) - * s.bol? # => false - * s.scan(/st\n/) - * s.bol? # => true - * s.terminate - * s.bol? # => true + * + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * beginning_of_line? -> true or false + * + * Returns whether the [position][2] is at the beginning of a line; + * that is, at the beginning of the [stored string][1] + * or immediately after a newline: + * + * scanner = StringScanner.new(MULTILINE_TEXT) + * scanner.string + * # => "Go placidly amid the noise and haste,\nand remember what peace there may be in silence.\n" + * scanner.pos # => 0 + * scanner.beginning_of_line? # => true + * + * scanner.scan_until(/,/) # => "Go placidly amid the noise and haste," + * scanner.beginning_of_line? # => false + * + * scanner.scan(/\n/) # => "\n" + * scanner.beginning_of_line? # => true + * + * scanner.terminate + * scanner.beginning_of_line? # => true + * + * scanner.concat('x') + * scanner.terminate + * scanner.beginning_of_line? # => false + * + * StringScanner#bol? is an alias for StringScanner#beginning_of_line?. */ static VALUE strscan_bol_p(VALUE self) @@ -1033,14 +1454,24 @@ strscan_bol_p(VALUE self) } /* - * Returns +true+ if the scan pointer is at the end of the string. + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * eos? -> true or false + * + * Returns whether the [position][2] + * is at the end of the [stored string][1]: + * + * ```rb + * scanner = StringScanner.new('foobarbaz') + * scanner.eos? # => false + * pos = 3 + * scanner.eos? # => false + * scanner.terminate + * scanner.eos? # => true + * ``` * - * s = StringScanner.new('test string') - * p s.eos? # => false - * s.scan(/test/) - * p s.eos? # => false - * s.terminate - * p s.eos? # => true */ static VALUE strscan_eos_p(VALUE self) @@ -1052,25 +1483,18 @@ strscan_eos_p(VALUE self) } /* - * Equivalent to #eos?. - * This method is obsolete, use #eos? instead. - */ -static VALUE -strscan_empty_p(VALUE self) -{ - rb_warning("StringScanner#empty? is obsolete; use #eos? instead"); - return strscan_eos_p(self); -} - -/* + * call-seq: + * rest? + * * Returns true if and only if there is more data in the string. See #eos?. - * This method is obsolete; use #eos? instead. * * s = StringScanner.new('test string') * # These two are opposites * s.eos? # => false * s.rest? # => true */ + + /* :nodoc: */ static VALUE strscan_rest_p(VALUE self) { @@ -1081,13 +1505,26 @@ strscan_rest_p(VALUE self) } /* - * Returns +true+ if and only if the last match was successful. + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * matched? -> true or false + * + * Returns `true` of the most recent [match attempt][17] was successful, + * `false` otherwise; + * see [Basic Matched Values][18]: + * + * ```rb + * scanner = StringScanner.new('foobarbaz') + * scanner.matched? # => false + * scanner.pos = 3 + * scanner.exist?(/baz/) # => 6 + * scanner.matched? # => true + * scanner.exist?(/nope/) # => nil + * scanner.matched? # => false + * ``` * - * s = StringScanner.new('test string') - * s.match?(/\w+/) # => 4 - * s.matched? # => true - * s.match?(/\d+/) # => nil - * s.matched? # => false */ static VALUE strscan_matched_p(VALUE self) @@ -1099,11 +1536,27 @@ strscan_matched_p(VALUE self) } /* - * Returns the last matched string. + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * matched -> matched_substring or nil + * + * Returns the matched substring from the most recent [match][17] attempt + * if it was successful, + * or `nil` otherwise; + * see [Basic Matched Values][18]: + * + * ```rb + * scanner = StringScanner.new('foobarbaz') + * scanner.matched # => nil + * scanner.pos = 3 + * scanner.match?(/bar/) # => 3 + * scanner.matched # => "bar" + * scanner.match?(/nope/) # => nil + * scanner.matched # => nil + * ``` * - * s = StringScanner.new('test string') - * s.match?(/\w+/) # -> 4 - * s.matched # -> "test" */ static VALUE strscan_matched(VALUE self) @@ -1118,15 +1571,29 @@ strscan_matched(VALUE self) } /* - * Returns the size of the most recent match in bytes, or +nil+ if there - * was no recent match. This is different than <tt>matched.size</tt>, - * which will return the size in characters. + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * matched_size -> substring_size or nil + * + * Returns the size (in bytes) of the matched substring + * from the most recent match [match attempt][17] if it was successful, + * or `nil` otherwise; + * see [Basic Matched Values][18]: + * + * ```rb + * scanner = StringScanner.new('foobarbaz') + * scanner.matched_size # => nil + * + * pos = 3 + * scanner.exist?(/baz/) # => 9 + * scanner.matched_size # => 3 + * + * scanner.exist?(/nope/) # => nil + * scanner.matched_size # => nil + * ``` * - * s = StringScanner.new('test string') - * s.check /\w+/ # -> "test" - * s.matched_size # -> 4 - * s.check /\d+/ # -> nil - * s.matched_size # -> nil */ static VALUE strscan_matched_size(VALUE self) @@ -1141,46 +1608,89 @@ strscan_matched_size(VALUE self) static int name_to_backref_number(struct re_registers *regs, VALUE regexp, const char* name, const char* name_end, rb_encoding *enc) { - int num; - - num = onig_name_to_backref_number(RREGEXP_PTR(regexp), - (const unsigned char* )name, (const unsigned char* )name_end, regs); - if (num >= 1) { - return num; - } - else { - rb_enc_raise(enc, rb_eIndexError, "undefined group name reference: %.*s", - rb_long2int(name_end - name), name); + if (RTEST(regexp)) { + int num = onig_name_to_backref_number(RREGEXP_PTR(regexp), + (const unsigned char* )name, + (const unsigned char* )name_end, + regs); + if (num >= 1) { + return num; + } } - - UNREACHABLE; + rb_enc_raise(enc, rb_eIndexError, "undefined group name reference: %.*s", + rb_long2int(name_end - name), name); } /* - * call-seq: [](n) - * - * Returns the n-th subgroup in the most recent match. - * - * s = StringScanner.new("Fri Dec 12 1975 14:39") - * s.scan(/(\w+) (\w+) (\d+) /) # -> "Fri Dec 12 " - * s[0] # -> "Fri Dec 12 " - * s[1] # -> "Fri" - * s[2] # -> "Dec" - * s[3] # -> "12" - * s.post_match # -> "1975 14:39" - * s.pre_match # -> "" - * - * s.reset - * s.scan(/(?<wday>\w+) (?<month>\w+) (?<day>\d+) /) # -> "Fri Dec 12 " - * s[0] # -> "Fri Dec 12 " - * s[1] # -> "Fri" - * s[2] # -> "Dec" - * s[3] # -> "12" - * s[:wday] # -> "Fri" - * s[:month] # -> "Dec" - * s[:day] # -> "12" - * s.post_match # -> "1975 14:39" - * s.pre_match # -> "" + * + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * [](specifier) -> substring or nil + * + * Returns a captured substring or `nil`; + * see [Captured Match Values][13]. + * + * When there are captures: + * + * ```rb + * scanner = StringScanner.new('Fri Dec 12 1975 14:39') + * scanner.scan(/(?<wday>\w+) (?<month>\w+) (?<day>\d+) /) + * ``` + * + * - `specifier` zero: returns the entire matched substring: + * + * ```rb + * scanner[0] # => "Fri Dec 12 " + * scanner.pre_match # => "" + * scanner.post_match # => "1975 14:39" + * ``` + * + * - `specifier` positive integer. returns the `n`th capture, or `nil` if out of range: + * + * ```rb + * scanner[1] # => "Fri" + * scanner[2] # => "Dec" + * scanner[3] # => "12" + * scanner[4] # => nil + * ``` + * + * - `specifier` negative integer. counts backward from the last subgroup: + * + * ```rb + * scanner[-1] # => "12" + * scanner[-4] # => "Fri Dec 12 " + * scanner[-5] # => nil + * ``` + * + * - `specifier` symbol or string. returns the named subgroup, or `nil` if no such: + * + * ```rb + * scanner[:wday] # => "Fri" + * scanner['wday'] # => "Fri" + * scanner[:month] # => "Dec" + * scanner[:day] # => "12" + * scanner[:nope] # => nil + * ``` + * + * When there are no captures, only `[0]` returns non-`nil`: + * + * ```rb + * scanner = StringScanner.new('foobarbaz') + * scanner.exist?(/bar/) + * scanner[0] # => "bar" + * scanner[1] # => nil + * ``` + * + * For a failed match, even `[0]` returns `nil`: + * + * ```rb + * scanner.scan(/nope/) # => nil + * scanner[0] # => nil + * scanner[1] # => nil + * ``` + * */ static VALUE strscan_aref(VALUE self, VALUE idx) @@ -1197,7 +1707,6 @@ strscan_aref(VALUE self, VALUE idx) idx = rb_sym2str(idx); /* fall through */ case T_STRING: - if (!RTEST(p->regex)) return Qnil; RSTRING_GETMEM(idx, name, i); i = name_to_backref_number(&(p->regs), p->regex, name, name + i, rb_enc_get(idx)); break; @@ -1217,14 +1726,28 @@ strscan_aref(VALUE self, VALUE idx) } /* - * call-seq: size + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * size -> captures_count + * + * Returns the count of captures if the most recent match attempt succeeded, `nil` otherwise; + * see [Captures Match Values][13]: * - * Returns the amount of subgroups in the most recent match. - * The full match counts as a subgroup. + * ```rb + * scanner = StringScanner.new('Fri Dec 12 1975 14:39') + * scanner.size # => nil + * + * pattern = /(?<wday>\w+) (?<month>\w+) (?<day>\d+) / + * scanner.match?(pattern) + * scanner.values_at(*0..scanner.size) # => ["Fri Dec 12 ", "Fri", "Dec", "12", nil] + * scanner.size # => 4 + * + * scanner.match?(/nope/) # => nil + * scanner.size # => nil + * ``` * - * s = StringScanner.new("Fri Dec 12 1975 14:39") - * s.scan(/(\w+) (\w+) (\d+) /) # -> "Fri Dec 12 " - * s.size # -> 4 */ static VALUE strscan_size(VALUE self) @@ -1237,16 +1760,30 @@ strscan_size(VALUE self) } /* - * call-seq: captures + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * captures -> substring_array or nil + * + * Returns the array of [captured match values][13] at indexes `(1..)` + * if the most recent match attempt succeeded, or `nil` otherwise: + * + * ```rb + * scanner = StringScanner.new('Fri Dec 12 1975 14:39') + * scanner.captures # => nil * - * Returns the subgroups in the most recent match (not including the full match). - * If nothing was priorly matched, it returns nil. + * scanner.exist?(/(?<wday>\w+) (?<month>\w+) (?<day>\d+) /) + * scanner.captures # => ["Fri", "Dec", "12"] + * scanner.values_at(*0..4) # => ["Fri Dec 12 ", "Fri", "Dec", "12", nil] + * + * scanner.exist?(/Fri/) + * scanner.captures # => [] + * + * scanner.scan(/nope/) + * scanner.captures # => nil + * ``` * - * s = StringScanner.new("Fri Dec 12 1975 14:39") - * s.scan(/(\w+) (\w+) (\d+) (1980)?/) # -> "Fri Dec 12 " - * s.captures # -> ["Fri", "Dec", "12", nil] - * s.scan(/(\w+) (\w+) (\d+) (1980)?/) # -> nil - * s.captures # -> nil */ static VALUE strscan_captures(VALUE self) @@ -1276,17 +1813,25 @@ strscan_captures(VALUE self) } /* - * call-seq: - * scanner.values_at( i1, i2, ... iN ) -> an_array + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * values_at(*specifiers) -> array_of_captures or nil + * + * Returns an array of captured substrings, or `nil` of none. * - * Returns the subgroups in the most recent match at the given indices. - * If nothing was priorly matched, it returns nil. + * For each `specifier`, the returned substring is `[specifier]`; + * see #[]. + * + * ```rb + * scanner = StringScanner.new('Fri Dec 12 1975 14:39') + * pattern = /(?<wday>\w+) (?<month>\w+) (?<day>\d+) / + * scanner.match?(pattern) + * scanner.values_at(*0..3) # => ["Fri Dec 12 ", "Fri", "Dec", "12"] + * scanner.values_at(*%i[wday month day]) # => ["Fri", "Dec", "12"] + * ``` * - * s = StringScanner.new("Fri Dec 12 1975 14:39") - * s.scan(/(\w+) (\w+) (\d+) /) # -> "Fri Dec 12 " - * s.values_at 0, -1, 5, 2 # -> ["Fri Dec 12 ", "12", nil, "Dec"] - * s.scan(/(\w+) (\w+) (\d+) /) # -> nil - * s.values_at 0, -1, 5, 2 # -> nil */ static VALUE @@ -1308,13 +1853,29 @@ strscan_values_at(int argc, VALUE *argv, VALUE self) } /* - * Returns the <i><b>pre</b>-match</i> (in the regular expression sense) of the last scan. + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * pre_match -> substring + * + * Returns the substring that precedes the matched substring + * from the most recent match attempt if it was successful, + * or `nil` otherwise; + * see [Basic Match Values][18]: + * + * ```rb + * scanner = StringScanner.new('foobarbaz') + * scanner.pre_match # => nil + * + * scanner.pos = 3 + * scanner.exist?(/baz/) # => 6 + * scanner.pre_match # => "foobar" # Substring of entire string, not just target string. + * + * scanner.exist?(/nope/) # => nil + * scanner.pre_match # => nil + * ``` * - * s = StringScanner.new('test string') - * s.scan(/\w+/) # -> "test" - * s.scan(/\s+/) # -> " " - * s.pre_match # -> "test" - * s.post_match # -> "string" */ static VALUE strscan_pre_match(VALUE self) @@ -1329,13 +1890,29 @@ strscan_pre_match(VALUE self) } /* - * Returns the <i><b>post</b>-match</i> (in the regular expression sense) of the last scan. + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * post_match -> substring + * + * Returns the substring that follows the matched substring + * from the most recent match attempt if it was successful, + * or `nil` otherwise; + * see [Basic Match Values][18]: + * + * ```rb + * scanner = StringScanner.new('foobarbaz') + * scanner.post_match # => nil + * + * scanner.pos = 3 + * scanner.match?(/bar/) # => 3 + * scanner.post_match # => "baz" + * + * scanner.match?(/nope/) # => nil + * scanner.post_match # => nil + * ``` * - * s = StringScanner.new('test string') - * s.scan(/\w+/) # -> "test" - * s.scan(/\s+/) # -> " " - * s.pre_match # -> "test" - * s.post_match # -> "string" */ static VALUE strscan_post_match(VALUE self) @@ -1350,8 +1927,24 @@ strscan_post_match(VALUE self) } /* - * Returns the "rest" of the string (i.e. everything after the scan pointer). - * If there is no more data (eos? = true), it returns <tt>""</tt>. + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * rest -> target_substring + * + * Returns the 'rest' of the [stored string][1] (all after the current [position][2]), + * which is the [target substring][3]: + * + * ```rb + * scanner = StringScanner.new('foobarbaz') + * scanner.rest # => "foobarbaz" + * scanner.pos = 3 + * scanner.rest # => "barbaz" + * scanner.terminate + * scanner.rest # => "" + * ``` + * */ static VALUE strscan_rest(VALUE self) @@ -1366,7 +1959,26 @@ strscan_rest(VALUE self) } /* - * <tt>s.rest_size</tt> is equivalent to <tt>s.rest.size</tt>. + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * rest_size -> integer + * + * Returns the size (in bytes) of the #rest of the [stored string][1]: + * + * ```rb + * scanner = StringScanner.new('foobarbaz') + * scanner.rest # => "foobarbaz" + * scanner.rest_size # => 9 + * scanner.pos = 3 + * scanner.rest # => "barbaz" + * scanner.rest_size # => 6 + * scanner.terminate + * scanner.rest # => "" + * scanner.rest_size # => 0 + * ``` + * */ static VALUE strscan_rest_size(VALUE self) @@ -1382,29 +1994,42 @@ strscan_rest_size(VALUE self) return INT2FIX(i); } -/* - * <tt>s.restsize</tt> is equivalent to <tt>s.rest_size</tt>. - * This method is obsolete; use #rest_size instead. - */ -static VALUE -strscan_restsize(VALUE self) -{ - rb_warning("StringScanner#restsize is obsolete; use #rest_size instead"); - return strscan_rest_size(self); -} - #define INSPECT_LENGTH 5 /* - * Returns a string that represents the StringScanner object, showing: - * - the current position - * - the size of the string - * - the characters surrounding the scan pointer - * - * s = StringScanner.new("Fri Dec 12 1975 14:39") - * s.inspect # -> '#<StringScanner 0/21 @ "Fri D...">' - * s.scan_until /12/ # -> "Fri Dec 12" - * s.inspect # -> '#<StringScanner 10/21 "...ec 12" @ " 1975...">' + * :markup: markdown + * :include: strscan/link_refs.txt + * + * call-seq: + * inspect -> string + * + * Returns a string representation of `self` that may show: + * + * 1. The current [position][2]. + * 2. The size (in bytes) of the [stored string][1]. + * 3. The substring preceding the current position. + * 4. The substring following the current position (which is also the [target substring][3]). + * + * ```rb + * scanner = StringScanner.new("Fri Dec 12 1975 14:39") + * scanner.pos = 11 + * scanner.inspect # => "#<StringScanner 11/21 \"...c 12 \" @ \"1975 ...\">" + * ``` + * + * If at beginning-of-string, item 4 above (following substring) is omitted: + * + * ```rb + * scanner.reset + * scanner.inspect # => "#<StringScanner 0/21 @ \"Fri D...\">" + * ``` + * + * If at end-of-string, all items above are omitted: + * + * ```rb + * scanner.terminate + * scanner.inspect # => "#<StringScanner fin>" + * ``` + * */ static VALUE strscan_inspect(VALUE self) @@ -1476,13 +2101,13 @@ inspect2(struct strscanner *p) } /* - * call-seq: - * scanner.fixed_anchor? -> true or false + * :markup: markdown + * :include: strscan/link_refs.txt * - * Whether +scanner+ uses fixed anchor mode or not. + * call-seq: + * fixed_anchor? -> true or false * - * If fixed anchor mode is used, +\A+ always matches the beginning of - * the string. Otherwise, +\A+ always matches the current position. + * Returns whether the [fixed-anchor property][10] is set. */ static VALUE strscan_fixed_anchor_p(VALUE self) @@ -1511,28 +2136,49 @@ named_captures_iter(const OnigUChar *name, VALUE value = RUBY_Qnil; int i; for (i = 0; i < back_num; i++) { - value = strscan_aref(data->self, INT2NUM(back_refs[i])); + VALUE v = strscan_aref(data->self, INT2NUM(back_refs[i])); + if (!RB_NIL_P(v)) { + value = v; + } } rb_hash_aset(data->captures, key, value); return 0; } /* + * :markup: markdown + * :include: strscan/link_refs.txt + * * call-seq: - * scanner.named_captures -> hash + * named_captures -> hash + * + * Returns the array of captured match values at indexes (1..) + * if the most recent match attempt succeeded, or nil otherwise; + * see [Captured Match Values][13]: * - * Returns a hash of string variables matching the regular expression. + * ```rb + * scanner = StringScanner.new('Fri Dec 12 1975 14:39') + * scanner.named_captures # => {} + * + * pattern = /(?<wday>\w+) (?<month>\w+) (?<day>\d+) / + * scanner.match?(pattern) + * scanner.named_captures # => {"wday"=>"Fri", "month"=>"Dec", "day"=>"12"} + * + * scanner.string = 'nope' + * scanner.match?(pattern) + * scanner.named_captures # => {"wday"=>nil, "month"=>nil, "day"=>nil} + * + * scanner.match?(/nosuch/) + * scanner.named_captures # => {} + * ``` * - * scan = StringScanner.new('foobarbaz') - * scan.match?(/(?<f>foo)(?<r>bar)(?<z>baz)/) - * scan.named_captures # -> {"f"=>"foo", "r"=>"bar", "z"=>"baz"} */ static VALUE strscan_named_captures(VALUE self) { struct strscanner *p; - GET_SCANNER(self, p); named_captures_data data; + GET_SCANNER(self, p); data.self = self; data.captures = rb_hash_new(); if (!RB_NIL_P(p->regex)) { @@ -1547,109 +2193,20 @@ strscan_named_captures(VALUE self) ======================================================================= */ /* - * Document-class: StringScanner - * - * StringScanner provides for lexical scanning operations on a String. Here is - * an example of its usage: - * - * require 'strscan' - * - * s = StringScanner.new('This is an example string') - * s.eos? # -> false - * - * p s.scan(/\w+/) # -> "This" - * p s.scan(/\w+/) # -> nil - * p s.scan(/\s+/) # -> " " - * p s.scan(/\s+/) # -> nil - * p s.scan(/\w+/) # -> "is" - * s.eos? # -> false - * - * p s.scan(/\s+/) # -> " " - * p s.scan(/\w+/) # -> "an" - * p s.scan(/\s+/) # -> " " - * p s.scan(/\w+/) # -> "example" - * p s.scan(/\s+/) # -> " " - * p s.scan(/\w+/) # -> "string" - * s.eos? # -> true + * Document-class: StringScanner::Error * - * p s.scan(/\s+/) # -> nil - * p s.scan(/\w+/) # -> nil - * - * Scanning a string means remembering the position of a <i>scan pointer</i>, - * which is just an index. The point of scanning is to move forward a bit at - * a time, so matches are sought after the scan pointer; usually immediately - * after it. - * - * Given the string "test string", here are the pertinent scan pointer - * positions: - * - * t e s t s t r i n g - * 0 1 2 ... 1 - * 0 - * - * When you #scan for a pattern (a regular expression), the match must occur - * at the character after the scan pointer. If you use #scan_until, then the - * match can occur anywhere after the scan pointer. In both cases, the scan - * pointer moves <i>just beyond</i> the last character of the match, ready to - * scan again from the next character onwards. This is demonstrated by the - * example above. - * - * == Method Categories - * - * There are other methods besides the plain scanners. You can look ahead in - * the string without actually scanning. You can access the most recent match. - * You can modify the string being scanned, reset or terminate the scanner, - * find out or change the position of the scan pointer, skip ahead, and so on. - * - * === Advancing the Scan Pointer - * - * - #getch - * - #get_byte - * - #scan - * - #scan_until - * - #skip - * - #skip_until - * - * === Looking Ahead - * - * - #check - * - #check_until - * - #exist? - * - #match? - * - #peek - * - * === Finding Where we Are - * - * - #beginning_of_line? (<tt>#bol?</tt>) - * - #eos? - * - #rest? - * - #rest_size - * - #pos - * - * === Setting Where we Are - * - * - #reset - * - #terminate - * - #pos= - * - * === Match Data - * - * - #matched - * - #matched? - * - #matched_size - * - <tt>#[]</tt> - * - #pre_match - * - #post_match + * The error class for StringScanner. + * See StringScanner#unscan. + */ + +/* + * Document-class: StringScanner * - * === Miscellaneous + * :markup: markdown * - * - <tt><<</tt> - * - #concat - * - #string - * - #string= - * - #unscan + * :include: strscan/link_refs.txt + * :include: strscan/strscan.md * - * There are aliases to several of the methods. */ void Init_strscan(void) @@ -1662,12 +2219,15 @@ Init_strscan(void) ID id_scanerr = rb_intern("ScanError"); VALUE tmp; - id_byteslice = rb_intern("byteslice"); + usascii_encindex = rb_usascii_encindex(); + utf8_encindex = rb_utf8_encindex(); + binary_encindex = rb_ascii8bit_encindex(); StringScanner = rb_define_class("StringScanner", rb_cObject); ScanError = rb_define_class_under(StringScanner, "Error", rb_eStandardError); if (!rb_const_defined(rb_cObject, id_scanerr)) { rb_const_set(rb_cObject, id_scanerr, ScanError); + rb_deprecate_constant(rb_cObject, "ScanError"); } tmp = rb_str_new2(STRSCAN_VERSION); rb_obj_freeze(tmp); @@ -1675,6 +2235,7 @@ Init_strscan(void) tmp = rb_str_new2("$Id$"); rb_obj_freeze(tmp); rb_const_set(StringScanner, rb_intern("Id"), tmp); + rb_deprecate_constant(StringScanner, "Id"); rb_define_alloc_func(StringScanner, strscan_s_allocate); rb_define_private_method(StringScanner, "initialize", strscan_initialize, -1); @@ -1682,7 +2243,6 @@ Init_strscan(void) rb_define_singleton_method(StringScanner, "must_C_version", strscan_s_mustc, 0); rb_define_method(StringScanner, "reset", strscan_reset, 0); rb_define_method(StringScanner, "terminate", strscan_terminate, 0); - rb_define_method(StringScanner, "clear", strscan_clear, 0); rb_define_method(StringScanner, "string", strscan_get_string, 0); rb_define_method(StringScanner, "string=", strscan_set_string, 1); rb_define_method(StringScanner, "concat", strscan_concat, 1); @@ -1707,16 +2267,18 @@ Init_strscan(void) rb_define_method(StringScanner, "getch", strscan_getch, 0); rb_define_method(StringScanner, "get_byte", strscan_get_byte, 0); - rb_define_method(StringScanner, "getbyte", strscan_getbyte, 0); + rb_define_method(StringScanner, "scan_byte", strscan_scan_byte, 0); rb_define_method(StringScanner, "peek", strscan_peek, 1); - rb_define_method(StringScanner, "peep", strscan_peep, 1); + rb_define_method(StringScanner, "peek_byte", strscan_peek_byte, 0); + + rb_define_private_method(StringScanner, "scan_base10_integer", strscan_scan_base10_integer, 0); + rb_define_private_method(StringScanner, "scan_base16_integer", strscan_scan_base16_integer, 0); rb_define_method(StringScanner, "unscan", strscan_unscan, 0); rb_define_method(StringScanner, "beginning_of_line?", strscan_bol_p, 0); rb_alias(StringScanner, rb_intern("bol?"), rb_intern("beginning_of_line?")); rb_define_method(StringScanner, "eos?", strscan_eos_p, 0); - rb_define_method(StringScanner, "empty?", strscan_empty_p, 0); rb_define_method(StringScanner, "rest?", strscan_rest_p, 0); rb_define_method(StringScanner, "matched?", strscan_matched_p, 0); @@ -1731,11 +2293,12 @@ Init_strscan(void) rb_define_method(StringScanner, "rest", strscan_rest, 0); rb_define_method(StringScanner, "rest_size", strscan_rest_size, 0); - rb_define_method(StringScanner, "restsize", strscan_restsize, 0); rb_define_method(StringScanner, "inspect", strscan_inspect, 0); rb_define_method(StringScanner, "fixed_anchor?", strscan_fixed_anchor_p, 0); rb_define_method(StringScanner, "named_captures", strscan_named_captures, 0); + + rb_require("strscan/strscan"); } diff --git a/ext/strscan/strscan.gemspec b/ext/strscan/strscan.gemspec index 8a61c7abe6..47180bb8d8 100644 --- a/ext/strscan/strscan.gemspec +++ b/ext/strscan/strscan.gemspec @@ -19,16 +19,24 @@ Gem::Specification.new do |s| files = [ "COPYING", "LICENSE.txt", + "lib/strscan/strscan.rb" ] + + s.require_paths = %w{lib} + if RUBY_ENGINE == "jruby" - s.require_paths = %w{ext/jruby/lib lib} - files << "ext/jruby/lib/strscan.rb" files << "lib/strscan.jar" + files << "ext/jruby/lib/strscan.rb" + s.require_paths += %w{ext/jruby/lib} s.platform = "java" else - s.require_paths = %w{lib} files << "ext/strscan/extconf.rb" files << "ext/strscan/strscan.c" + s.rdoc_options << "-idoc" + s.extra_rdoc_files = [ + ".rdoc_options", + *Dir.glob("doc/strscan/**/*") + ] s.extensions = %w{ext/strscan/extconf.rb} end s.files = files diff --git a/ext/win32/lib/win32/registry.rb b/ext/win32/lib/win32/registry.rb index 16a08310ad..734d6987a2 100644 --- a/ext/win32/lib/win32/registry.rb +++ b/ext/win32/lib/win32/registry.rb @@ -1,84 +1,75 @@ -# frozen_string_literal: false +# frozen_string_literal: true require 'fiddle/import' module Win32 - -=begin rdoc -= Win32 Registry - -win32/registry is registry accessor library for Win32 platform. -It uses importer to call Win32 Registry APIs. - -== example - Win32::Registry::HKEY_CURRENT_USER.open('SOFTWARE\foo') do |reg| - value = reg['foo'] # read a value - value = reg['foo', Win32::Registry::REG_SZ] # read a value with type - type, value = reg.read('foo') # read a value - reg['foo'] = 'bar' # write a value - reg['foo', Win32::Registry::REG_SZ] = 'bar' # write a value with type - reg.write('foo', Win32::Registry::REG_SZ, 'bar') # write a value - - reg.each_value { |name, type, data| ... } # Enumerate values - reg.each_key { |key, wtime| ... } # Enumerate subkeys - - reg.delete_value(name) # Delete a value - reg.delete_key(name) # Delete a subkey - reg.delete_key(name, true) # Delete a subkey recursively - end - -= Reference - -== Win32::Registry class - ---- info - ---- num_keys - ---- max_key_length - ---- num_values - ---- max_value_name_length - ---- max_value_length - ---- descriptor_length - ---- wtime - Returns an item of key information. - -=== constants ---- HKEY_CLASSES_ROOT - ---- HKEY_CURRENT_USER - ---- HKEY_LOCAL_MACHINE - ---- HKEY_PERFORMANCE_DATA - ---- HKEY_CURRENT_CONFIG - ---- HKEY_DYN_DATA - - Win32::Registry object whose key is predefined key. -For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/predefined_keys.asp] article. - -=end rdoc - + # :stopdoc: WCHAR = Encoding::UTF_16LE WCHAR_NUL = "\0".encode(WCHAR).freeze WCHAR_CR = "\r".encode(WCHAR).freeze WCHAR_SIZE = WCHAR_NUL.bytesize - LOCALE = Encoding.find(Encoding.locale_charmap) - + LOCALE = Encoding::UTF_8 + + # :startdoc: + + # win32/registry is registry accessor library for Win32 platform. + # It uses importer to call Win32 Registry APIs. + # + # == example + # Win32::Registry::HKEY_CURRENT_USER.open('SOFTWARE\foo') do |reg| + # value = reg['foo'] # read a value + # value = reg['foo', Win32::Registry::REG_SZ] # read a value with type + # type, value = reg.read('foo') # read a value + # reg['foo'] = 'bar' # write a value + # reg['foo', Win32::Registry::REG_SZ] = 'bar' # write a value with type + # reg.write('foo', Win32::Registry::REG_SZ, 'bar') # write a value + # + # reg.each_value { |name, type, data| ... } # Enumerate values + # reg.each_key { |key, wtime| ... } # Enumerate subkeys + # + # reg.delete_value(name) # Delete a value + # reg.delete_key(name) # Delete a subkey + # reg.delete_key(name, true) # Delete a subkey recursively + # end + # + # == Predefined keys + # + # * +HKEY_CLASSES_ROOT+ + # * +HKEY_CURRENT_USER+ + # * +HKEY_LOCAL_MACHINE+ + # * +HKEY_PERFORMANCE_DATA+ + # * +HKEY_CURRENT_CONFIG+ + # * +HKEY_DYN_DATA+ + # + # Win32::Registry object whose key is predefined key. + # For detail, see the article[https://learn.microsoft.com/en-us/windows/win32/sysinfo/predefined-keys]. + # + # == Value types + # + # * +REG_NONE+ + # * +REG_SZ+ + # * +REG_EXPAND_SZ+ + # * +REG_BINARY+ + # * +REG_DWORD+ + # * +REG_DWORD_BIG_ENDIAN+ + # * +REG_LINK+ + # * +REG_MULTI_SZ+ + # * +REG_RESOURCE_LIST+ + # * +REG_FULL_RESOURCE_DESCRIPTOR+ + # * +REG_RESOURCE_REQUIREMENTS_LIST+ + # * +REG_QWORD+ + # + # For detail, see the article[https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-value-types]. + # class Registry + # :stopdoc: + # # For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/registry.asp]. # # --- HKEY_* # - # Predefined key ((*handle*)). + # Predefined key *handle*. # These are Integer, not Win32::Registry. # # --- REG_* @@ -100,6 +91,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr # If the key is created newly or opened existing key. # See also Registry#disposition method. module Constants + # :stopdoc: HKEY_CLASSES_ROOT = 0x80000000 HKEY_CURRENT_USER = 0x80000001 HKEY_LOCAL_MACHINE = 0x80000002 @@ -115,7 +107,6 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr REG_EXPAND_SZ = 2 REG_BINARY = 3 REG_DWORD = 4 - REG_DWORD_LITTLE_ENDIAN = 4 REG_DWORD_BIG_ENDIAN = 5 REG_LINK = 6 REG_MULTI_SZ = 7 @@ -163,22 +154,29 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr end include Constants include Enumerable + # :startdoc: # # Error # class Error < ::StandardError + # :stopdoc: module Kernel32 extend Fiddle::Importer dlload "kernel32.dll" end FormatMessageW = Kernel32.extern "int FormatMessageW(int, void *, int, int, void *, int, void *)", :stdcall + # :startdoc: + + # new(code) -> error object + # + # Initializes the message for Win32 API error +code+. def initialize(code) @code = code buff = WCHAR_NUL * 1024 lang = 0 begin - len = FormatMessageW.call(0x1200, 0, code, lang, buff, 1024, 0) + len = FormatMessageW.call(0x1200, nil, code, lang, buff, 1024, nil) msg = buff.byteslice(0, len * WCHAR_SIZE) msg.delete!(WCHAR_CR) msg.chomp! @@ -190,6 +188,8 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr end super msg end + + # Win32 API error code. attr_reader :code end @@ -197,8 +197,9 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr # Predefined Keys # class PredefinedKey < Registry + # :stopdoc: def initialize(hkey, keyname) - @hkey = hkey + @hkey = Fiddle::Pointer.new(hkey) @parent = nil @keyname = keyname @disposition = REG_OPENED_EXISTING_KEY @@ -224,6 +225,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr # Win32 APIs # module API + # :stopdoc: include Constants extend Fiddle::Importer dlload "advapi32.dll" @@ -238,7 +240,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr "long RegDeleteKeyW(void *, void *)", "long RegFlushKey(void *)", "long RegCloseKey(void *)", - "long RegQueryInfoKey(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *)", + "long RegQueryInfoKeyW(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *)", ].each do |fn| cfunc = extern fn, :stdcall const_set cfunc.name.intern, cfunc @@ -254,34 +256,38 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr /^(?:x64|x86_64)/ =~ RUBY_PLATFORM end + TEMPLATE_HANDLE = 'J<' + def packhandle(h) - win64? ? packqw(h) : packdw(h) + [h].pack(TEMPLATE_HANDLE) end def unpackhandle(h) - win64? ? unpackqw(h) : unpackdw(h) + (h + [0].pack(TEMPLATE_HANDLE)).unpack1(TEMPLATE_HANDLE) end + TEMPLATE_DWORD = 'V' + def packdw(dw) - [dw].pack('V') + [dw].pack(TEMPLATE_DWORD) end def unpackdw(dw) - dw += [0].pack('V') - dw.unpack('V')[0] + (dw + [0].pack(TEMPLATE_DWORD)).unpack1(TEMPLATE_DWORD) end + TEMPLATE_QWORD = 'Q<' + def packqw(qw) - [ qw & 0xFFFFFFFF, qw >> 32 ].pack('VV') + [qw].pack(TEMPLATE_QWORD) end def unpackqw(qw) - qw = qw.unpack('VV') - (qw[1] << 32) | qw[0] + (qw + [0].pack(TEMPLATE_QWORD)).unpack1(TEMPLATE_QWORD) end def make_wstr(str) - str.encode(WCHAR) + (str+"\0").encode(WCHAR) end def OpenKey(hkey, name, opt, desired) @@ -294,14 +300,14 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr result = packhandle(0) disp = packdw(0) check RegCreateKeyExW.call(hkey, make_wstr(name), 0, 0, opt, desired, - 0, result, disp) + nil, result, disp) [ unpackhandle(result), unpackdw(disp) ] end def EnumValue(hkey, index) name = WCHAR_NUL * Constants::MAX_KEY_LENGTH size = packdw(Constants::MAX_KEY_LENGTH) - check RegEnumValueW.call(hkey, index, name, size, 0, 0, 0, 0) + check RegEnumValueW.call(hkey, index, name, size, nil, nil, nil, nil) name.byteslice(0, unpackdw(size) * WCHAR_SIZE) end @@ -309,7 +315,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr name = WCHAR_NUL * Constants::MAX_KEY_LENGTH size = packdw(Constants::MAX_KEY_LENGTH) wtime = ' ' * 8 - check RegEnumKeyExW.call(hkey, index, name, size, 0, 0, 0, wtime) + check RegEnumKeyExW.call(hkey, index, name, size, nil, nil, nil, wtime) [ name.byteslice(0, unpackdw(size) * WCHAR_SIZE), unpackqw(wtime) ] end @@ -317,9 +323,9 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr type = packdw(0) size = packdw(0) name = make_wstr(name) - check RegQueryValueExW.call(hkey, name, 0, type, 0, size) - data = "\0".force_encoding('ASCII-8BIT') * unpackdw(size) - check RegQueryValueExW.call(hkey, name, 0, type, data, size) + check RegQueryValueExW.call(hkey, name, nil, type, nil, size) + data = "\0".b * unpackdw(size) + check RegQueryValueExW.call(hkey, name, nil, type, data, size) [ unpackdw(type), data[0, unpackdw(size)] ] end @@ -356,25 +362,26 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr maxvaluelen = packdw(0) secdescs = packdw(0) wtime = ' ' * 8 - check RegQueryInfoKey.call(hkey, 0, 0, 0, subkeys, maxsubkeylen, 0, + check RegQueryInfoKeyW.call(hkey, 0, 0, 0, subkeys, maxsubkeylen, 0, values, maxvaluenamelen, maxvaluelen, secdescs, wtime) [ unpackdw(subkeys), unpackdw(maxsubkeylen), unpackdw(values), unpackdw(maxvaluenamelen), unpackdw(maxvaluelen), unpackdw(secdescs), unpackqw(wtime) ] end end + # :startdoc: # - # Replace %\w+% into the environment value of what is contained between the %'s + # Replace <tt>%</tt>-enclosed substrings in +str+ into the + # environment value of what is contained between the <tt>%</tt>s. # This method is used for REG_EXPAND_SZ. # - # For detail, see expandEnvironmentStrings[http://msdn.microsoft.com/library/en-us/sysinfo/base/expandenvironmentstrings.asp] \Win32 \API. + # For detail, see ExpandEnvironmentStrings[https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-expandenvironmentstringsw] \Win32 \API. # def self.expand_environ(str) str.gsub(Regexp.compile("%([^%]+)%".encode(str.encoding))) { v = $1.encode(LOCALE) - (e = ENV[v] || ENV[v.upcase]; e.encode(str.encoding) if e) || - $& + (ENV[v] || ENV[v.upcase])&.encode(str.encoding) || $& } end @@ -384,27 +391,26 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr REG_RESOURCE_LIST REG_FULL_RESOURCE_DESCRIPTOR REG_RESOURCE_REQUIREMENTS_LIST REG_QWORD ].inject([]) do |ary, type| - type.freeze ary[Constants.const_get(type)] = type ary end.freeze # - # Convert registry type value to readable string. + # Convert registry type value +type+ to readable string. # def self.type2name(type) @@type2name[type] || type.to_s end # - # Convert 64-bit FILETIME integer into Time object. + # Convert 64-bit FILETIME integer +wtime+ into Time object. # def self.wtime2time(wtime) Time.at((wtime - 116444736000000000) / 10000000) end # - # Convert Time object or Integer object into 64-bit FILETIME. + # Convert Time object or Integer object +time+ into 64-bit FILETIME. # def self.time2wtime(time) time.to_i * 10000000 + 116444736000000000 @@ -416,19 +422,22 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr private_class_method :new # - # --- Registry.open(key, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED) + # call-seq: + # open(key, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED) + # open(key, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED) { |reg| ... } # - # --- Registry.open(key, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED) { |reg| ... } + # Open the registry key +subkey+ under +key+. + # +key+ is Win32::Registry object of parent key. + # You can use {predefined key}[rdoc-ref:Win32::Registry@Predefined+keys] +HKEY_+*. + # +desired+ and +opt+ is access mask and key option. # - # Open the registry key subkey under key. - # key is Win32::Registry object of parent key. - # You can use predefined key HKEY_* (see Constants) - # desired and opt is access mask and key option. # For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/regopenkeyex.asp]. - # If block is given, the key is closed automatically. + # + # If block is given, the key +reg+ is yielded and closed + # automatically after the block exists. def self.open(hkey, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED) subkey = subkey.chomp('\\') - newkey = API.OpenKey(hkey.hkey, subkey, opt, desired) + newkey = API.OpenKey(hkey.instance_variable_get(:@hkey), subkey, opt, desired) obj = new(newkey, hkey, subkey, REG_OPENED_EXISTING_KEY) if block_given? begin @@ -442,20 +451,22 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr end # - # --- Registry.create(key, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED) + # call-seq: + # create(key, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED) + # create(key, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED) { |reg| ... } # - # --- Registry.create(key, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED) { |reg| ... } + # Create or open the registry key +subkey+ under +key+. + # You can use {predefined key}[rdoc-ref:Win32::Registry@Predefined+keys] +HKEY_+*. + # +desired+ and +opt+ is access mask and key option. # - # Create or open the registry key subkey under key. - # You can use predefined key HKEY_* (see Constants) - # - # If subkey is already exists, key is opened and Registry#created? + # If +subkey+ is already exists, key is opened and Registry#created? # method will return false. # - # If block is given, the key is closed automatically. + # If block is given, the key +reg+ is yielded and closed + # automatically after the block exists. # def self.create(hkey, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED) - newkey, disp = API.CreateKey(hkey.hkey, subkey, opt, desired) + newkey, disp = API.CreateKey(hkey.instance_variable_get(:@hkey), subkey, opt, desired) obj = new(newkey, hkey, subkey, disp) if block_given? begin @@ -474,10 +485,12 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr @@final = proc { |hkey| proc { API.CloseKey(hkey[0]) if hkey[0] } } # - # initialize + # :nodoc: + # + # Use self.open, self.create, #open and #create. # def initialize(hkey, parent, keyname, disposition) - @hkey = hkey + @hkey = Fiddle::Pointer.new(hkey) @parent = parent @keyname = keyname @disposition = disposition @@ -485,9 +498,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr ObjectSpace.define_finalizer self, @@final.call(@hkeyfinal) end - # Returns key handle value. - attr_reader :hkey - # Win32::Registry object of parent key, or nil if predefeined key. + # Win32::Registry object of parent key, or nil if predefined key. attr_reader :parent # Same as subkey value of Registry.open or # Registry.create method. @@ -495,8 +506,13 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr # Disposition value (REG_CREATED_NEW_KEY or REG_OPENED_EXISTING_KEY). attr_reader :disposition + # Returns key handle value. + def hkey + @hkey.to_i + end + # - # Returns if key is created ((*newly*)). + # Returns +true+ if key is created *newly*. # (see Registry.create) -- basically you call create # then when you call created? on the instance returned # it will tell if it was successful or not @@ -513,7 +529,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr end # - # Full path of key such as 'HKEY_CURRENT_USER\SOFTWARE\foo\bar'. + # Full path of key such as <tt>'HKEY_CURRENT_USER\SOFTWARE\foo\bar'</tt>. # def name parent = self @@ -524,6 +540,9 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr name end + # + # Retruns inspected string + # def inspect "\#<Win32::Registry key=#{name.inspect}>" end @@ -536,14 +555,14 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr end # - # Same as Win32::Registry.open (self, subkey, desired, opt) + # Same as Win32::Registry.open(self, subkey, desired, opt) # def open(subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED, &blk) self.class.open(self, subkey, desired, opt, &blk) end # - # Same as Win32::Registry.create (self, subkey, desired, opt) + # Same as Win32::Registry.create(self, subkey, desired, opt) # def create(subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED, &blk) self.class.create(self, subkey, desired, opt, &blk) @@ -561,9 +580,16 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr end # - # Enumerate values. + # Enumerate all values in this registry path. + # + # For each value it yields key, type and data. + # + # key is a String which contains name of key. + # type is a type constant kind of +Win32::Registry::REG_+* + # data is the value of this key. # def each_value + return enum_for(:each_value) unless block_given? index = 0 while true begin @@ -594,13 +620,16 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr end # - # Enumerate subkeys. + # Enumerate all subkeys. + # + # For each subkey it yields subkey and wtime. # # subkey is String which contains name of subkey. # wtime is last write time as FILETIME (64-bit integer). # (see Registry.wtime2time) # def each_key + return enum_for(:each_key) unless block_given? index = 0 while true begin @@ -625,21 +654,23 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr end # Read a registry value named name and return array of - # [ type, data ]. - # When name is nil, the `default' value is read. - # type is value type. (see Win32::Registry::Constants module) - # data is value data, its class is: - # :REG_SZ, REG_EXPAND_SZ + # <tt>[ type, data ]</tt>. + # When name is +nil+, the `default' value is read. + # + # +type+ is {value type}[rdoc-ref:Win32::Registry@Value+types]. + # + # +data+ is value data, its class is: + # REG_SZ, REG_EXPAND_SZ:: # String - # :REG_MULTI_SZ + # REG_MULTI_SZ:: # Array of String - # :REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_QWORD + # REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_QWORD:: # Integer - # :REG_BINARY, REG_NONE + # REG_BINARY, REG_NONE:: # String (contains binary data) # - # When rtype is specified, the value type must be included by - # rtype array, or TypeError is raised. + # When _rtype_ is specified, the value type must be included by + # _rtype_ array, or +TypeError+ is raised. def read(name, *rtype) type, data = API.QueryValue(@hkey, name) unless rtype.empty? or rtype.include?(type) @@ -657,7 +688,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr when REG_DWORD [ type, API.unpackdw(data) ] when REG_DWORD_BIG_ENDIAN - [ type, data.unpack('N')[0] ] + [ type, data.unpack1('N') ] when REG_QWORD [ type, API.unpackqw(data) ] else @@ -672,9 +703,9 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr # If the value type is REG_EXPAND_SZ, returns value data whose environment # variables are replaced. # If the value type is neither REG_SZ, REG_MULTI_SZ, REG_DWORD, - # REG_DWORD_BIG_ENDIAN, nor REG_QWORD, TypeError is raised. + # REG_DWORD_BIG_ENDIAN, nor REG_QWORD, +TypeError+ is raised. # - # The meaning of rtype is the same as for the #read method. + # The meaning of _rtype_ is the same as for the #read method. # def [](name, *rtype) type, data = read(name, *rtype) @@ -691,7 +722,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr # Read a REG_SZ(read_s), REG_DWORD(read_i), or REG_BINARY(read_bin) # registry value named name. # - # If the values type does not match, TypeError is raised. + # If the values type does not match, +TypeError+ is raised. def read_s(name) read(name, REG_SZ)[1] end @@ -700,7 +731,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr # Read a REG_SZ or REG_EXPAND_SZ registry value named name. # # If the value type is REG_EXPAND_SZ, environment variables are replaced. - # Unless the value type is REG_SZ or REG_EXPAND_SZ, TypeError is raised. + # Unless the value type is REG_SZ or REG_EXPAND_SZ, +TypeError+ is raised. # def read_s_expand(name) type, data = read(name, REG_SZ, REG_EXPAND_SZ) @@ -715,7 +746,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr # Read a REG_SZ(read_s), REG_DWORD(read_i), or REG_BINARY(read_bin) # registry value named name. # - # If the values type does not match, TypeError is raised. + # If the values type does not match, +TypeError+ is raised. # def read_i(name) read(name, REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_QWORD)[1] @@ -725,7 +756,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr # Read a REG_SZ(read_s), REG_DWORD(read_i), or REG_BINARY(read_bin) # registry value named name. # - # If the values type does not match, TypeError is raised. + # If the values type does not match, +TypeError+ is raised. # def read_bin(name) read(name, REG_BINARY)[1] @@ -735,7 +766,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr # Write data to a registry value named name. # When name is nil, write to the `default' value. # - # type is type value. (see Registry::Constants module) + # +type+ is {value type}[rdoc-ref:Win32::Registry@Value+types]. # Class of data must be same as which #read # method returns. # @@ -764,11 +795,12 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr # # If wtype is specified, the value type is it. # Otherwise, the value type is depend on class of value: - # :Integer + # + # Integer:: # REG_DWORD - # :String + # String:: # REG_SZ - # :Array + # Array:: # REG_MULTI_SZ # def []=(name, rtype, value = nil) @@ -865,19 +897,19 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr # # Returns key information as Array of: - # :num_keys + # num_keys:: # The number of subkeys. - # :max_key_length + # max_key_length:: # Maximum length of name of subkeys. - # :num_values + # num_values:: # The number of values. - # :max_value_name_length + # max_value_name_length:: # Maximum length of name of values. - # :max_value_length + # max_value_length:: # Maximum length of value of values. - # :descriptor_length + # descriptor_length:: # Length of security descriptor. - # :wtime + # wtime:: # Last write time as FILETIME(64-bit integer) # # For detail, see RegQueryInfoKey[http://msdn.microsoft.com/library/en-us/sysinfo/base/regqueryinfokey.asp] Win32 API. diff --git a/ext/win32/lib/win32/resolv.rb b/ext/win32/lib/win32/resolv.rb index d06658f0aa..8d631a2140 100644 --- a/ext/win32/lib/win32/resolv.rb +++ b/ext/win32/lib/win32/resolv.rb @@ -4,12 +4,22 @@ =end -require 'win32/registry' +require 'win32/resolv.so' module Win32 module Resolv - API = Registry::API - Error = Registry::Error + # Error at Win32 API + class Error < StandardError + # +code+ Win32 Error code + # +message+ Formatted message for +code+ + def initialize(code, message) + super(message) + @code = code + end + + # Win32 error code + attr_reader :code + end def self.get_hosts_path path = get_hosts_dir @@ -34,98 +44,59 @@ module Win32 end [ search, nameserver ] end - end -end - -begin - require 'win32/resolv.so' -rescue LoadError -end - -module Win32 -#==================================================================== -# Windows NT -#==================================================================== - module Resolv - module SZ - refine Registry do - # ad hoc workaround for broken registry - def read_s(key) - type, str = read(key) - unless type == Registry::REG_SZ - warn "Broken registry, #{name}\\#{key} was #{Registry.type2name(type)}, ignored" - return String.new - end - str - end - end - end - using SZ - - TCPIP_NT = 'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters' class << self private def get_hosts_dir - Registry::HKEY_LOCAL_MACHINE.open(TCPIP_NT) do |reg| - reg.read_s_expand('DataBasePath') + tcpip_params do |params| + params.value('DataBasePath') end end def get_info search = nil nameserver = get_dns_server_list - Registry::HKEY_LOCAL_MACHINE.open(TCPIP_NT) do |reg| - begin - slist = reg.read_s('SearchList') - search = slist.split(/,\s*/) unless slist.empty? - rescue Registry::Error - end + + tcpip_params do |params| + slist = params.value('SearchList') + search = slist.split(/,\s*/) if slist and !slist.empty? if add_search = search.nil? search = [] - begin - nvdom = reg.read_s('NV Domain') - unless nvdom.empty? - @search = [ nvdom ] - if reg.read_i('UseDomainNameDevolution') != 0 - if /^\w+\./ =~ nvdom - devo = $' - end + domain = params.value('Domain') + + if domain and !domain.empty? + search = [ domain ] + udmnd = params.value('UseDomainNameDevolution') + if udmnd&.nonzero? + if /^\w+\./ =~ domain + devo = $' end end - rescue Registry::Error end end - reg.open('Interfaces') do |h| - h.each_key do |iface, | - h.open(iface) do |regif| - next unless ns = %w[NameServer DhcpNameServer].find do |key| - begin - ns = regif.read_s(key) - rescue Registry::Error - else - break ns.split(/[,\s]\s*/) unless ns.empty? - end - end - next if (nameserver & ns).empty? + params.open('Interfaces') do |reg| + reg.each_key do |iface| + next unless ns = %w[NameServer DhcpNameServer].find do |key| + ns = iface.value(key) + break ns.split(/[,\s]\s*/) if ns and !ns.empty? + end + + next if (nameserver & ns).empty? - if add_search - begin - [ 'Domain', 'DhcpDomain' ].each do |key| - dom = regif.read_s(key) - unless dom.empty? - search.concat(dom.split(/,\s*/)) - break - end - end - rescue Registry::Error + if add_search + [ 'Domain', 'DhcpDomain' ].each do |key| + dom = iface.value(key) + if dom and !dom.empty? + search.concat(dom.split(/,\s*/)) + break end end end end end + search << devo if add_search and devo end [ search.uniq, nameserver.uniq ] diff --git a/ext/win32/lib/win32/sspi.rb b/ext/win32/lib/win32/sspi.rb deleted file mode 100644 index 20205fd4d6..0000000000 --- a/ext/win32/lib/win32/sspi.rb +++ /dev/null @@ -1,338 +0,0 @@ -# frozen_string_literal: false -# -# = win32/sspi.rb -# -# Copyright (c) 2006-2007 Justin Bailey -# -# Written and maintained by Justin Bailey <jgbailey@gmail.com>. -# -# This program is free software. You can re-distribute and/or -# modify this program under the same terms of ruby itself --- -# Ruby Distribution License or GNU General Public License. -# - -require 'fiddle/import' - -# Implements bindings to Win32 SSPI functions, focused on authentication to a proxy server over HTTP. -module Win32 - module SSPI - # Specifies how credential structure requested will be used. Only SECPKG_CRED_OUTBOUND is used - # here. - SECPKG_CRED_INBOUND = 0x00000001 - SECPKG_CRED_OUTBOUND = 0x00000002 - SECPKG_CRED_BOTH = 0x00000003 - - # Format of token. NETWORK format is used here. - SECURITY_NATIVE_DREP = 0x00000010 - SECURITY_NETWORK_DREP = 0x00000000 - - # InitializeSecurityContext Requirement flags - ISC_REQ_REPLAY_DETECT = 0x00000004 - ISC_REQ_SEQUENCE_DETECT = 0x00000008 - ISC_REQ_CONFIDENTIALITY = 0x00000010 - ISC_REQ_USE_SESSION_KEY = 0x00000020 - ISC_REQ_PROMPT_FOR_CREDS = 0x00000040 - ISC_REQ_CONNECTION = 0x00000800 - - # Win32 API Functions. Uses Win32API to bind methods to constants contained in class. - module API - extend Fiddle::Importer - dlload "secur32.dll" - [ - # Can be called with AcquireCredentialsHandleA.call() - "unsigned long AcquireCredentialsHandleA(void *, void *, unsigned long, void *, void *, void *, void *, void *, void *)", - # Can be called with InitializeSecurityContextA.call() - "unsigned long InitializeSecurityContextA(void *, void *, void *, unsigned long, unsigned long, unsigned long, void *, unsigned long, void *, void *, void *, void *)", - # Can be called with DeleteSecurityContext.call() - "unsigned long DeleteSecurityContext(void *)", - # Can be called with FreeCredentialsHandle.call() - "unsigned long FreeCredentialsHandle(void *)" - ].each do |fn| - cfunc = extern fn, :stdcall - const_set cfunc.name.intern, cfunc - end - end - - # SecHandle struct - class SecurityHandle - def upper - @struct.unpack("LL")[1] - end - - def lower - @struct.unpack("LL")[0] - end - - def to_p - @struct ||= "\0" * 8 - end - end - - # Some familiar aliases for the SecHandle structure - CredHandle = CtxtHandle = SecurityHandle - - # TimeStamp struct - class TimeStamp - attr_reader :struct - - def to_p - @struct ||= "\0" * 8 - end - end - - # Creates binary representations of a SecBufferDesc structure, - # including the SecBuffer contained inside. - class SecurityBuffer - - SECBUFFER_TOKEN = 2 # Security token - - TOKENBUFSIZE = 12288 - SECBUFFER_VERSION = 0 - - def initialize(buffer = nil) - @buffer = buffer || "\0" * TOKENBUFSIZE - @bufferSize = @buffer.length - @type = SECBUFFER_TOKEN - end - - def bufferSize - unpack - @bufferSize - end - - def bufferType - unpack - @type - end - - def token - unpack - @buffer - end - - def to_p - # Assumption is that when to_p is called we are going to get a packed structure. Therefore, - # set @unpacked back to nil so we know to unpack when accessors are next accessed. - @unpacked = nil - # Assignment of inner structure to variable is very important here. Without it, - # will not be able to unpack changes to the structure. Alternative, nested unpacks, - # does not work (i.e. @struct.unpack("LLP12")[2].unpack("LLP12") results in "no associated pointer") - @sec_buffer ||= [@bufferSize, @type, @buffer].pack("LLP") - @struct ||= [SECBUFFER_VERSION, 1, @sec_buffer].pack("LLP") - end - - private - - # Unpacks the SecurityBufferDesc structure into member variables. We - # only want to do this once per struct, so the struct is deleted - # after unpacking. - def unpack - if ! @unpacked && @sec_buffer && @struct - @bufferSize, @type = @sec_buffer.unpack("LL") - @buffer = @sec_buffer.unpack("LLP#{@bufferSize}")[2] - @struct = nil - @sec_buffer = nil - @unpacked = true - end - end - end - - # SEC_WINNT_AUTH_IDENTITY structure - class Identity - SEC_WINNT_AUTH_IDENTITY_ANSI = 0x1 - - attr_accessor :user, :domain, :password - - def initialize(user = nil, domain = nil, password = nil) - @user = user - @domain = domain - @password = password - @flags = SEC_WINNT_AUTH_IDENTITY_ANSI - end - - def to_p - [@user, @user ? @user.length : 0, - @domain, @domain ? @domain.length : 0, - @password, @password ? @password.length : 0, - @flags].pack("PLPLPLL") - end - end - - # Takes a return result from an SSPI function and interprets the value. - class SSPIResult - # Good results - SEC_E_OK = 0x00000000 - SEC_I_CONTINUE_NEEDED = 0x00090312 - - # These are generally returned by InitializeSecurityContext - SEC_E_INSUFFICIENT_MEMORY = 0x80090300 - SEC_E_INTERNAL_ERROR = 0x80090304 - SEC_E_INVALID_HANDLE = 0x80090301 - SEC_E_INVALID_TOKEN = 0x80090308 - SEC_E_LOGON_DENIED = 0x8009030C - SEC_E_NO_AUTHENTICATING_AUTHORITY = 0x80090311 - SEC_E_NO_CREDENTIALS = 0x8009030E - SEC_E_TARGET_UNKNOWN = 0x80090303 - SEC_E_UNSUPPORTED_FUNCTION = 0x80090302 - SEC_E_WRONG_PRINCIPAL = 0x80090322 - - # These are generally returned by AcquireCredentialsHandle - SEC_E_NOT_OWNER = 0x80090306 - SEC_E_SECPKG_NOT_FOUND = 0x80090305 - SEC_E_UNKNOWN_CREDENTIALS = 0x8009030D - - @@map = {} - constants.each { |v| @@map[self.const_get(v.to_s)] = v } - - attr_reader :value - - def initialize(value) - # convert to unsigned long - value = [value].pack("L").unpack("L").first - raise "#{value.to_s(16)} is not a recognized result" unless @@map.has_key? value - @value = value - end - - def to_s - @@map[@value].to_s - end - - def ok? - @value == SEC_I_CONTINUE_NEEDED || @value == SEC_E_OK - end - - def ==(other) - if other.is_a?(SSPIResult) - @value == other.value - elsif other.is_a?(Fixnum) - @value == @@map[other] - else - false - end - end - end - - # Handles "Negotiate" type authentication. Geared towards authenticating with a proxy server over HTTP - class NegotiateAuth - attr_accessor :credentials, :context, :contextAttributes, :user, :domain - - # Default request flags for SSPI functions - REQUEST_FLAGS = ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION - - # NTLM tokens start with this header always. Encoding alone adds "==" and newline, so remove those - B64_TOKEN_PREFIX = ["NTLMSSP"].pack("m").delete("=\n") - - # Given a connection and a request path, performs authentication as the current user and returns - # the response from a GET request. The connection should be a Net::HTTP object, and it should - # have been constructed using the Net::HTTP.Proxy method, but anything that responds to "get" will work. - # If a user and domain are given, will authenticate as the given user. - # Returns the response received from the get method (usually Net::HTTPResponse) - def NegotiateAuth.proxy_auth_get(http, path, user = nil, domain = nil) - raise "http must respond to :get" unless http.respond_to?(:get) - nego_auth = self.new user, domain - - resp = http.get path, { "Proxy-Authorization" => "Negotiate " + nego_auth.get_initial_token } - if resp["Proxy-Authenticate"] - resp = http.get path, { "Proxy-Authorization" => "Negotiate " + nego_auth.complete_authentication(resp["Proxy-Authenticate"].split(" ").last.strip) } - end - - resp - end - - # Creates a new instance ready for authentication as the given user in the given domain. - # Defaults to current user and domain as defined by ENV["USERDOMAIN"] and ENV["USERNAME"] if - # no arguments are supplied. - def initialize(user = nil, domain = nil) - if user.nil? && domain.nil? && ENV["USERNAME"].nil? && ENV["USERDOMAIN"].nil? - raise "A username or domain must be supplied since they cannot be retrieved from the environment" - end - - @user = user || ENV["USERNAME"] - @domain = domain || ENV["USERDOMAIN"] - end - - # Gets the initial Negotiate token. Returns it as a base64 encoded string suitable for use in HTTP. Can - # be easily decoded, however. - def get_initial_token - raise "This object is no longer usable because its resources have been freed." if @cleaned_up - get_credentials - - outputBuffer = SecurityBuffer.new - @context = CtxtHandle.new - @contextAttributes = "\0" * 4 - - result = SSPIResult.new(API::InitializeSecurityContextA.call(@credentials.to_p, nil, nil, - REQUEST_FLAGS,0, SECURITY_NETWORK_DREP, nil, 0, @context.to_p, outputBuffer.to_p, @contextAttributes, TimeStamp.new.to_p)) - - if result.ok? then - return encode_token(outputBuffer.token) - else - raise "Error: #{result.to_s}" - end - end - - # Takes a token and gets the next token in the Negotiate authentication chain. Token can be Base64 encoded or not. - # The token can include the "Negotiate" header and it will be stripped. - # Does not indicate if SEC_I_CONTINUE or SEC_E_OK was returned. - # Token returned is Base64 encoded w/ all new lines removed. - def complete_authentication(token) - raise "This object is no longer usable because its resources have been freed." if @cleaned_up - - # Nil token OK, just set it to empty string - token = "" if token.nil? - - if token.include? "Negotiate" - # If the Negotiate prefix is passed in, assume we are seeing "Negotiate <token>" and get the token. - token = token.split(" ").last - end - - if token.include? B64_TOKEN_PREFIX - # indicates base64 encoded token - token = token.strip.unpack("m")[0] - end - - outputBuffer = SecurityBuffer.new - result = SSPIResult.new(API::InitializeSecurityContext.call(@credentials.to_p, @context.to_p, nil, - REQUEST_FLAGS, 0, SECURITY_NETWORK_DREP, SecurityBuffer.new(token).to_p, 0, - @context.to_p, - outputBuffer.to_p, @contextAttributes, TimeStamp.new.to_p)) - - if result.ok? then - return encode_token(outputBuffer.token) - else - raise "Error: #{result.to_s}" - end - ensure - # need to make sure we don't clean up if we've already cleaned up. - clean_up unless @cleaned_up - end - - private - - def clean_up - # free structures allocated - @cleaned_up = true - API::FreeCredentialsHandle.call(@credentials.to_p) - API::DeleteSecurityContext.call(@context.to_p) - @context = nil - @credentials = nil - @contextAttributes = nil - end - - # Gets credentials based on user, domain or both. If both are nil, an error occurs - def get_credentials - @credentials = CredHandle.new - ts = TimeStamp.new - @identity = Identity.new @user, @domain - result = SSPIResult.new(API::AcquireCredentialsHandleA.call(nil, "Negotiate", SECPKG_CRED_OUTBOUND, nil, @identity.to_p, - nil, nil, @credentials.to_p, ts.to_p)) - raise "Error acquire credentials: #{result}" unless result.ok? - end - - def encode_token(t) - # encode64 will add newlines every 60 characters so we need to remove those. - [t].pack("m").delete("\n") - end - end - end -end diff --git a/ext/win32/resolv/extconf.rb b/ext/win32/resolv/extconf.rb index 01f3df730a..5ee4c0d7c4 100644 --- a/ext/win32/resolv/extconf.rb +++ b/ext/win32/resolv/extconf.rb @@ -1,3 +1,7 @@ -if have_library('iphlpapi', 'GetNetworkParams') +require 'mkmf' +if RUBY_ENGINE == "ruby" and have_library('iphlpapi', 'GetNetworkParams', ['windows.h', 'iphlpapi.h']) + have_library('advapi32', 'RegGetValueW', ['windows.h']) create_makefile('win32/resolv') +else + File.write('Makefile', "all clean install:\n\t@echo Done: $(@)\n") end diff --git a/ext/win32/resolv/resolv.c b/ext/win32/resolv/resolv.c index 8a50ef7824..b2d377df9f 100644 --- a/ext/win32/resolv/resolv.c +++ b/ext/win32/resolv/resolv.c @@ -1,22 +1,56 @@ #include <ruby.h> #include <ruby/encoding.h> #include <windows.h> +#include <windns.h> #ifndef NTDDI_VERSION #define NTDDI_VERSION 0x06000000 #endif #include <iphlpapi.h> +#ifndef numberof +#define numberof(array) ((int)(sizeof(array) / sizeof((array)[0]))) +#endif + static VALUE w32error_make_error(DWORD e) { - VALUE code = ULONG2NUM(e); - return rb_class_new_instance(1, &code, rb_path2class("Win32::Resolv::Error")); + char buffer[512], *p; + DWORD source = 0; + VALUE args[2]; + if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, &source, e, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + buffer, sizeof(buffer), NULL)) { + snprintf(buffer, sizeof(buffer), "Unknown Error %lu", (unsigned long)e); + } + p = buffer; + while ((p = strpbrk(p, "\r\n")) != NULL) { + memmove(p, p + 1, strlen(p)); + if (!p[1]) { + p[0] = '\0'; + break; + } + } + args[0] = ULONG2NUM(e); + args[1] = rb_str_new_cstr(buffer); + return rb_class_new_instance(2, args, rb_path2class("Win32::Resolv::Error")); } static void -w32error_raise(DWORD e) +w32error_check(DWORD e) +{ + if (e != NO_ERROR) { + rb_exc_raise(w32error_make_error(e)); + } +} + +static VALUE +wchar_to_utf8(const WCHAR *w, int n) { - rb_exc_raise(w32error_make_error(e)); + int clen = WideCharToMultiByte(CP_UTF8, 0, w, n, NULL, 0, NULL, NULL); + VALUE str = rb_enc_str_new(NULL, clen, rb_utf8_encoding()); + WideCharToMultiByte(CP_UTF8, 0, w, n, RSTRING_PTR(str), clen, NULL, NULL); + return str; } static VALUE @@ -28,9 +62,7 @@ get_dns_server_list(VALUE self) VALUE buf, nameservers = Qnil; ret = GetNetworkParams(NULL, &buflen); - if (ret != NO_ERROR && ret != ERROR_BUFFER_OVERFLOW) { - w32error_raise(ret); - } + if (ret != ERROR_BUFFER_OVERFLOW) w32error_check(ret); fixedinfo = ALLOCV(buf, buflen); ret = GetNetworkParams(fixedinfo, &buflen); if (ret == NO_ERROR) { @@ -44,18 +76,181 @@ get_dns_server_list(VALUE self) } while ((ipaddr = ipaddr->Next) != NULL); } ALLOCV_END(buf); - if (ret != NO_ERROR) w32error_raise(ret); + w32error_check(ret); return nameservers; } + +static const WCHAR TCPIP_Params[] = L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"; + +static void +hkey_finalize(void *p) +{ + RegCloseKey((HKEY)p); +} + +static const rb_data_type_t hkey_type = { + "RegKey", + {0, hkey_finalize}, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, +}; + +static VALUE +hkey_close(VALUE self) +{ + RegCloseKey((HKEY)DATA_PTR(self)); + DATA_PTR(self) = 0; + return self; +} + +static VALUE reg_key_class; + +static VALUE +reg_open_key(VALUE klass, HKEY hkey, const WCHAR *wname) +{ + VALUE k = TypedData_Wrap_Struct(klass, &hkey_type, NULL); + DWORD e = RegOpenKeyExW(hkey, wname, 0, KEY_READ, (HKEY *)&DATA_PTR(k)); + if (e == ERROR_FILE_NOT_FOUND) return Qnil; + w32error_check(e); + return rb_ensure(rb_yield, k, hkey_close, k); +} + +static VALUE +tcpip_params_open(VALUE klass) +{ + return reg_open_key(reg_key_class, HKEY_LOCAL_MACHINE, TCPIP_Params); +} + +static int +to_wname(VALUE *name, WCHAR *wname, int wlen) +{ + const char *n = StringValueCStr(*name); + int nlen = RSTRING_LEN(*name); + int len = MultiByteToWideChar(CP_UTF8, 0, n, nlen, wname, wlen - 1); + if (len == 0) w32error_check(GetLastError()); + if (len >= wlen) rb_raise(rb_eArgError, "too long name"); + wname[len] = L'\0'; + return len; +} + +static VALUE +reg_open(VALUE self, VALUE name) +{ + HKEY hkey = DATA_PTR(self); + WCHAR wname[256]; + to_wname(&name, wname, numberof(wname)); + return reg_open_key(CLASS_OF(self), hkey, wname); +} + + +static VALUE +reg_each_key(VALUE self) +{ + WCHAR wname[256]; + HKEY hkey = DATA_PTR(self); + VALUE k = TypedData_Wrap_Struct(CLASS_OF(self), &hkey_type, NULL); + DWORD i, e, n; + for (i = 0; n = numberof(wname), (e = RegEnumKeyExW(hkey, i, wname, &n, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS; i++) { + e = RegOpenKeyExW(hkey, wname, 0, KEY_READ, (HKEY *)&DATA_PTR(k)); + w32error_check(e); + rb_ensure(rb_yield, k, hkey_close, k); + } + if (e != ERROR_NO_MORE_ITEMS) w32error_check(e); + return self; +} + +static inline DWORD +swap_dw(DWORD x) +{ +#if defined(_MSC_VER) + return _byteswap_ulong(x); +#else + return __builtin_bswap32(x); +#endif +} + +static VALUE +reg_value(VALUE self, VALUE name) +{ + HKEY hkey = DATA_PTR(self); + DWORD type = 0, size = 0, e; + VALUE result, value_buffer; + void *buffer; + WCHAR wname[256]; + to_wname(&name, wname, numberof(wname)); + e = RegGetValueW(hkey, NULL, wname, RRF_RT_ANY, &type, NULL, &size); + if (e == ERROR_FILE_NOT_FOUND) return Qnil; + w32error_check(e); +# define get_value_2nd(data, dsize) do { \ + DWORD type2 = type; \ + w32error_check(RegGetValueW(hkey, NULL, wname, RRF_RT_ANY, &type2, data, dsize)); \ + if (type != type2) { \ + rb_raise(rb_eRuntimeError, "registry value type changed %lu -> %lu", \ + (unsigned long)type, (unsigned long)type2); \ + } \ + } while (0) + + switch (type) { + case REG_DWORD: case REG_DWORD_BIG_ENDIAN: + { + DWORD d; + if (size != sizeof(d)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", (unsigned long)size); + w32error_check(RegGetValueW(hkey, NULL, wname, RRF_RT_REG_DWORD, &type, &d, &size)); + if (type == REG_DWORD_BIG_ENDIAN) d = swap_dw(d); + return ULONG2NUM(d); + } + case REG_QWORD: + { + QWORD q; + if (size != sizeof(q)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", (unsigned long)size); + w32error_check(RegGetValueW(hkey, NULL, wname, RRF_RT_REG_QWORD, &type, &q, &size)); + return ULL2NUM(q); + } + case REG_SZ: case REG_MULTI_SZ: case REG_EXPAND_SZ: + if (size % sizeof(WCHAR)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", (unsigned long)size); + buffer = ALLOCV_N(char, value_buffer, size); + get_value_2nd(buffer, &size); + if (type == REG_MULTI_SZ) { + const WCHAR *w = (WCHAR *)buffer; + result = rb_ary_new(); + size /= sizeof(WCHAR); + size -= 1; + for (size_t i = 0; i < size; ++i) { + int n = lstrlenW(w+i); + rb_ary_push(result, wchar_to_utf8(w+i, n)); + i += n; + } + } + else { + result = wchar_to_utf8((WCHAR *)buffer, lstrlenW((WCHAR *)buffer)); + } + ALLOCV_END(value_buffer); + break; + default: + result = rb_str_new(0, size); + get_value_2nd(RSTRING_PTR(result), &size); + rb_str_set_len(result, size); + break; + } + return result; +} + void InitVM_resolv(void) { VALUE mWin32 = rb_define_module("Win32"); VALUE resolv = rb_define_module_under(mWin32, "Resolv"); VALUE singl = rb_singleton_class(resolv); + VALUE regkey = rb_define_class_under(resolv, "registry key", rb_cObject); + + reg_key_class = regkey; + rb_undef_alloc_func(regkey); rb_define_private_method(singl, "get_dns_server_list", get_dns_server_list, 0); + rb_define_private_method(singl, "tcpip_params", tcpip_params_open, 0); + rb_define_method(regkey, "open", reg_open, 1); + rb_define_method(regkey, "each_key", reg_each_key, 0); + rb_define_method(regkey, "value", reg_value, 1); } void diff --git a/ext/win32/win32-registry.gemspec b/ext/win32/win32-registry.gemspec new file mode 100644 index 0000000000..9bd57bd7d1 --- /dev/null +++ b/ext/win32/win32-registry.gemspec @@ -0,0 +1,29 @@ +# frozen_string_literal: true +Gem::Specification.new do |spec| + spec.name = "win32-registry" + spec.version = "0.1.2" + spec.authors = ["U.Nakamura"] + spec.email = ["usa@garbagecollect.jp"] + + spec.summary = %q{Provides an interface to the Windows Registry in Ruby} + spec.description = spec.summary + spec.homepage = "https://github.com/ruby/win32-registry" + spec.required_ruby_version = ">= 2.6.0" + + spec.metadata["homepage_uri"] = spec.homepage + spec.metadata["source_code_uri"] = spec.homepage + + # Specify which files should be added to the gem when it is released. + # The `git ls-files -z` loads the files in the RubyGem that have been added into git. + excludes = %w[ + bin/ test/ spec/ features/ rakelib/ + .git* .mailmap appveyor Rakefile Gemfile + ] + git_files = %w[git ls-files -z --] + excludes.map {|x| ":^/#{x}"} + spec.files = IO.popen(git_files, chdir: __dir__, &:read).split("\x0") + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] + + spec.add_dependency "fiddle", "~> 1.0" +end diff --git a/ext/win32ole/depend b/ext/win32ole/depend deleted file mode 100644 index c8445ad5fa..0000000000 --- a/ext/win32ole/depend +++ /dev/null @@ -1,12 +0,0 @@ -WIN32OLE_HEADERS = $(HDRS) $(ruby_headers) -win32ole.o : win32ole.c $(WIN32OLE_HEADERS) -win32ole_variant_m.o : win32ole_variant_m.c $(WIN32OLE_HEADERS) -win32ole_typelib.o : win32ole_typelib.c $(WIN32OLE_HEADERS) -win32ole_type.o : win32ole_type.c $(WIN32OLE_HEADERS) -win32ole_variable.o : win32ole_variable.c $(WIN32OLE_HEADERS) -win32ole_method.o : win32ole_method.c $(WIN32OLE_HEADERS) -win32ole_param.o : win32ole_param.c $(WIN32OLE_HEADERS) -win32ole_variant.o : win32ole_variant.c $(WIN32OLE_HEADERS) -win32ole_event.o : win32ole_event.c $(WIN32OLE_HEADERS) -win32ole_record.o : win32ole_record.c $(WIN32OLE_HEADERS) -win32ole_error.o : win32ole_error.c $(WIN32OLE_HEADERS) diff --git a/ext/win32ole/extconf.rb b/ext/win32ole/extconf.rb deleted file mode 100644 index d2044663a9..0000000000 --- a/ext/win32ole/extconf.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: false -#---------------------------------- -# extconf.rb -# $Revision$ -#---------------------------------- -require 'mkmf' - -case RUBY_PLATFORM -when /cygwin/ - inc = nil - lib = '/usr/lib/w32api' -end - -dir_config("win32", inc, lib) - -def create_win32ole_makefile - if have_library("ole32") and - have_library("oleaut32") and - have_library("uuid", "&CLSID_CMultiLanguage", "mlang.h") and - have_library("user32") and - have_library("kernel32") and - have_library("advapi32") and - have_header("windows.h") - unless have_type("IMultiLanguage2", "mlang.h") - have_type("IMultiLanguage", "mlang.h") - end - spec = nil - checking_for('thread_specific', '%s') do - spec = %w[__declspec(thread) __thread].find {|th| - try_compile("#{th} int foo;", "", :werror => true) - } - spec or 'no' - end - $defs << "-DRB_THREAD_SPECIFIC=#{spec}" if spec - create_makefile("win32ole") - end -end - - -case RUBY_PLATFORM -when /mswin/ - $CFLAGS.sub!(/((?:\A|\s)[-\/])W\d(?=\z|\s)/, '\1W3') or - $CFLAGS += ' -W3' -end -create_win32ole_makefile diff --git a/ext/win32ole/lib/win32ole.rb b/ext/win32ole/lib/win32ole.rb deleted file mode 100644 index 5e20f104cd..0000000000 --- a/ext/win32ole/lib/win32ole.rb +++ /dev/null @@ -1,32 +0,0 @@ -begin - require 'win32ole.so' -rescue LoadError - # do nothing -end - -if defined?(WIN32OLE) - class WIN32OLE - - # - # By overriding Object#methods, WIN32OLE might - # work well with did_you_mean gem. - # This is experimental. - # - # require 'win32ole' - # dict = WIN32OLE.new('Scripting.Dictionary') - # dict.Ade('a', 1) - # #=> Did you mean? Add - # - def methods(*args) - super + ole_methods_safely.map(&:name).map(&:to_sym) - end - - private - - def ole_methods_safely - ole_methods - rescue WIN32OLEQueryInterfaceError - [] - end - end -end diff --git a/ext/win32ole/lib/win32ole/property.rb b/ext/win32ole/lib/win32ole/property.rb deleted file mode 100644 index fea047cd19..0000000000 --- a/ext/win32ole/lib/win32ole/property.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: false -# OLEProperty -# helper class of Property with arguments. -class OLEProperty - def initialize(obj, dispid, gettypes, settypes) - @obj = obj - @dispid = dispid - @gettypes = gettypes - @settypes = settypes - end - def [](*args) - @obj._getproperty(@dispid, args, @gettypes) - end - def []=(*args) - @obj._setproperty(@dispid, args, @settypes) - end -end diff --git a/ext/win32ole/sample/excel1.rb b/ext/win32ole/sample/excel1.rb deleted file mode 100644 index 4fe1d0c2a9..0000000000 --- a/ext/win32ole/sample/excel1.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: false -require 'win32ole' - -application = WIN32OLE.new('Excel.Application') - -application.visible = true -workbook = application.Workbooks.Add(); -worksheet = workbook.Worksheets(1); - -=begin -worksheet.Range("A1:D1").value = ["North","South","East","West"]; -worksheet.Range("A2:B2").value = [5.2, 10]; - -worksheet.Range("C2").value = 8; -worksheet.Range("D2").value = 20; -=end - -worksheet.Range("A1:B2").value = [["North","South"], - [5.2, 10]]; - -vals = WIN32OLE_VARIANT.new([["East","West"], - [8, 20]], - WIN32OLE::VARIANT::VT_ARRAY) -worksheet.Range("C1:D2").value = vals - -range = worksheet.Range("A1:D2"); -range.Select -chart = workbook.Charts.Add; - -workbook.saved = true; - -print "Now quit Excel... Please enter." -gets - -application.ActiveWorkbook.Close(0); -application.Quit(); - diff --git a/ext/win32ole/sample/excel2.rb b/ext/win32ole/sample/excel2.rb deleted file mode 100644 index 47a5715f84..0000000000 --- a/ext/win32ole/sample/excel2.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: false -require 'win32ole' - -# -4100 is the value for the Excel constant xl3DColumn. -ChartTypeVal = -4100; - -# Creates OLE object to Excel -excel = WIN32OLE.new("excel.application") - -# Create and rotate the chart -excel.visible = true; -excel.Workbooks.Add(); -excel.Range("a1").value = 3; -excel.Range("a2").value = 2; -excel.Range("a3").value = 1; -excel.Range("a1:a3").Select(); -excelchart = excel.Charts.Add(); -excelchart.type = ChartTypeVal; - -i = 0 -i.step(180, 10) do |rot| - excelchart.rotation=rot; - sleep 0.1 -end -# Done, bye - -print "Now quit Excel... Please enter." -gets - -excel.ActiveWorkbook.Close(0); -excel.Quit(); diff --git a/ext/win32ole/sample/excel3.rb b/ext/win32ole/sample/excel3.rb deleted file mode 100644 index 72aee2a929..0000000000 --- a/ext/win32ole/sample/excel3.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: false -require 'win32ole' - -#application = WIN32OLE.new('Excel.Application.5') -application = WIN32OLE.new('Excel.Application') - -application.visible = true -workbook = application.Workbooks.Add(); -sheet = workbook.Worksheets(1); -sheetS = workbook.Worksheets -puts "The number of sheets is #{sheetS.count}" -puts "Now add 2 sheets after of `#{sheet.name}`" -sheetS.add({'count'=>2, 'after'=>sheet}) -puts "The number of sheets is #{sheetS.count}" - -print "Now quit Excel... Please enter." -gets - -application.ActiveWorkbook.Close(0); -application.Quit(); - diff --git a/ext/win32ole/sample/ie.rb b/ext/win32ole/sample/ie.rb deleted file mode 100644 index 4db64eed30..0000000000 --- a/ext/win32ole/sample/ie.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: false -require 'win32ole' -url = 'http://www.ruby-lang.org/' -ie = WIN32OLE.new('InternetExplorer.Application') -ie.visible = true -ie.gohome -print "Now navigate Ruby home page... Please enter." -gets -ie.navigate(url) -print "Now quit Internet Explorer... Please enter." -gets -ie.Quit() diff --git a/ext/win32ole/sample/ieconst.rb b/ext/win32ole/sample/ieconst.rb deleted file mode 100644 index 363a4f8153..0000000000 --- a/ext/win32ole/sample/ieconst.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: false -require 'win32ole' - -ie = WIN32OLE.new('InternetExplorer.Application') -=begin -WIN32OLE.const_load(ie) -WIN32OLE.constants.sort.each do |c| - puts "#{c} = #{WIN32OLE.const_get(c)}" -end -=end - -module IE_CONST -end - -WIN32OLE.const_load(ie, IE_CONST) -IE_CONST.constants.sort.each do |c| - puts "#{c} = #{IE_CONST.const_get(c)}" -end - -#------------------------------------------------------------ -# Remark!!! CONSTANTS has not tested enoughly!!! -# CONSTANTS is alpha release. -# If there are constants which first letter is not [a-zA-Z], -# like a '_Foo', then maybe you can access the value by -# using CONSTANTS['_Foo'] -#------------------------------------------------------------ -IE_CONST::CONSTANTS.each do |k, v| - puts "#{k} = #{v}" -end - -puts WIN32OLE::VERSION -ie.quit - diff --git a/ext/win32ole/sample/ienavi.rb b/ext/win32ole/sample/ienavi.rb deleted file mode 100644 index 5d0536028b..0000000000 --- a/ext/win32ole/sample/ienavi.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: false -require 'win32ole' - -$urls = [] - -def navigate(url) - $urls << url -end - -def stop_msg_loop - puts "Now Stop IE..." - $LOOP = false; -end - -def default_handler(event, *args) - case event - when "BeforeNavigate" - puts "Now Navigate #{args[0]}..." - end -end - -ie = WIN32OLE.new('InternetExplorer.Application') -ie.visible = true -ie.gohome - -ev = WIN32OLE_EVENT.new(ie, 'DWebBrowserEvents') - -ev.on_event {|*args| default_handler(*args)} -ev.on_event("NavigateComplete") {|url| navigate(url)} -ev.on_event("Quit") {|*args| stop_msg_loop} - -$LOOP = true -while ($LOOP) - WIN32OLE_EVENT.message_loop -end - -puts "You Navigated the URLs ..." -$urls.each_with_index do |url, i| - puts "(#{i+1}) #{url}" -end - diff --git a/ext/win32ole/sample/ienavi2.rb b/ext/win32ole/sample/ienavi2.rb deleted file mode 100644 index 3248393077..0000000000 --- a/ext/win32ole/sample/ienavi2.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: false -require 'win32ole' - -class IEHandler - attr_reader :loop - def initialize - @urls = [] - @loop = true - end - def method_missing(event, *args) - case event - when "BeforeNavigate2" - puts "Now Navigate #{args[1]}..." - end - end - def onNavigateComplete2(pdisp, url) - @urls << url - end - def onOnQuit - puts "Now Stop IE..." - @loop = false - end - def put_urls - puts "You Navigated the URLs ..." - @urls.each_with_index do |url, i| - puts "(#{i+1}) #{url}" - end - end -end - -ie = WIN32OLE.new('InternetExplorer.Application') -ie.visible = true -ie.gohome - -ev = WIN32OLE_EVENT.new(ie) -ev.handler = IEHandler.new - -while (ev.handler.loop) - WIN32OLE_EVENT.message_loop -end -ev.handler.put_urls diff --git a/ext/win32ole/sample/oledirs.rb b/ext/win32ole/sample/oledirs.rb deleted file mode 100644 index e52a0fd7ac..0000000000 --- a/ext/win32ole/sample/oledirs.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: false -# -# You need WSH(Windows Scripting Host) to run this script. -# - -require "win32ole" - -def listup(items) -# items.each do |i| - for i in items - puts i.name - end -end - -fs = WIN32OLE.new("Scripting.FileSystemObject") - -folder = fs.GetFolder(".") - -puts "--- folder of #{folder.path} ---" -listup(folder.SubFolders) - -puts "--- files of #{folder.path} ---" -listup(folder.Files) - diff --git a/ext/win32ole/sample/olegen.rb b/ext/win32ole/sample/olegen.rb deleted file mode 100644 index 4b088a774f..0000000000 --- a/ext/win32ole/sample/olegen.rb +++ /dev/null @@ -1,348 +0,0 @@ -# frozen_string_literal: false -#----------------------------- -# olegen.rb -# $Revision$ -#----------------------------- - -require 'win32ole' - -class WIN32COMGen - def initialize(typelib) - @typelib = typelib - @receiver = "" - end - attr_reader :typelib - - def ole_classes(typelib) - begin - @ole = WIN32OLE.new(typelib) - [@ole.ole_obj_help] - rescue - WIN32OLE_TYPE.ole_classes(typelib) - end - end - - def generate_args(method) - args = [] - if method.size_opt_params >= 0 - size_required_params = method.size_params - method.size_opt_params - else - size_required_params = method.size_params - 1 - end - size_required_params.times do |i| - if method.params[i] && method.params[i].optional? - args.push "arg#{i}=nil" - else - args.push "arg#{i}" - end - end - if method.size_opt_params >= 0 - method.size_opt_params.times do |i| - args.push "arg#{i + size_required_params}=nil" - end - else - args.push "*arg" - end - args.join(", ") - end - - def generate_argtype(typedetails) - ts = '' - typedetails.each do |t| - case t - when 'CARRAY', 'VOID', 'UINT', 'RESULT', 'DECIMAL', 'I8', 'UI8' -# raise "Sorry type\"" + t + "\" not supported" - ts << "\"??? NOT SUPPORTED TYPE:`#{t}'\"" - when 'USERDEFINED', 'Unknown Type 9' - ts << 'VT_DISPATCH' - break; - when 'SAFEARRAY' - ts << 'VT_ARRAY|' - when 'PTR' - ts << 'VT_BYREF|' - when 'INT' - ts << 'VT_I4' - else - if String === t - ts << 'VT_' + t - end - end - end - if ts.empty? - ts = 'VT_VARIANT' - elsif ts[-1] == ?| - ts += 'VT_VARIANT' - end - ts - end - - def generate_argtypes(method, proptypes) - types = method.params.collect{|param| - generate_argtype(param.ole_type_detail) - }.join(", ") - if proptypes - types += ", " if types.size > 0 - types += generate_argtype(proptypes) - end - types - end - - def generate_method_body(method, disptype, types=nil) - " ret = #{@receiver}#{disptype}(#{method.dispid}, [" + - generate_args(method).gsub("=nil", "") + - "], [" + - generate_argtypes(method, types) + - "])\n" + - " @lastargs = WIN32OLE::ARGV\n" + - " ret" - end - - def generate_method_help(method, type = nil) - str = " # " - if type - str += type - else - str += method.return_type - end - str += " #{method.name}" - if method.event? - str += " EVENT" - str += " in #{method.event_interface}" - end - if method.helpstring && method.helpstring != "" - str += "\n # " - str += method.helpstring - end - args_help = generate_method_args_help(method) - if args_help - str += "\n" - str += args_help - end - str - end - - def generate_method_args_help(method) - args = [] - method.params.each_with_index {|param, i| - h = " # #{param.ole_type} arg#{i} --- #{param.name}" - inout = [] - inout.push "IN" if param.input? - inout.push "OUT" if param.output? - h += " [#{inout.join('/')}]" - h += " ( = #{param.default})" if param.default - args.push h - } - if args.size > 0 - args.join("\n") - else - nil - end - end - - def generate_method(method, disptype, io = STDOUT, types = nil) - io.puts "\n" - io.puts generate_method_help(method) - if method.invoke_kind == 'PROPERTYPUT' - io.print " def #{method.name}=(" - else - io.print " def #{method.name}(" - end - io.print generate_args(method) - io.puts ")" - io.puts generate_method_body(method, disptype, types) - io.puts " end" - end - - def generate_propputref_methods(klass, io = STDOUT) - klass.ole_methods.select {|method| - method.invoke_kind == 'PROPERTYPUTREF' && method.visible? - }.each do |method| - generate_method(method, io) - end - end - - def generate_properties_with_args(klass, io = STDOUT) - klass.ole_methods.select {|method| - method.invoke_kind == 'PROPERTYGET' && - method.visible? && - method.size_params > 0 - }.each do |method| - types = method.return_type_detail - io.puts "\n" - io.puts generate_method_help(method, types[0]) - io.puts " def #{method.name}" - if klass.ole_type == "Class" - io.print " OLEProperty.new(@dispatch, #{method.dispid}, [" - else - io.print " OLEProperty.new(self, #{method.dispid}, [" - end - io.print generate_argtypes(method, nil) - io.print "], [" - io.print generate_argtypes(method, types) - io.puts "])" - io.puts " end" - end - end - - def generate_propput_methods(klass, io = STDOUT) - klass.ole_methods.select {|method| - method.invoke_kind == 'PROPERTYPUT' && method.visible? && - method.size_params == 1 - }.each do |method| - ms = klass.ole_methods.select {|m| - m.invoke_kind == 'PROPERTYGET' && - m.dispid == method.dispid - } - types = [] - if ms.size == 1 - types = ms[0].return_type_detail - end - generate_method(method, '_setproperty', io, types) - end - end - - def generate_propget_methods(klass, io = STDOUT) - klass.ole_methods.select {|method| - method.invoke_kind == 'PROPERTYGET' && method.visible? && - method.size_params == 0 - }.each do |method| - generate_method(method, '_getproperty', io) - end - end - - def generate_func_methods(klass, io = STDOUT) - klass.ole_methods.select {|method| - method.invoke_kind == "FUNC" && method.visible? - }.each do |method| - generate_method(method, '_invoke', io) - end - end - - def generate_methods(klass, io = STDOUT) - generate_propget_methods(klass, io) - generate_propput_methods(klass, io) - generate_properties_with_args(klass, io) - generate_func_methods(klass, io) -# generate_propputref_methods(klass, io) - end - - def generate_constants(klass, io = STDOUT) - klass.variables.select {|v| - v.visible? && v.variable_kind == 'CONSTANT' - }.each do |v| - io.print " " - io.print v.name.sub(/^./){$&.upcase} - io.print " = " - io.puts v.value - end - end - - def class_name(klass) - klass_name = klass.name - if klass.ole_type == "Class" && - klass.guid && - klass.progid - klass_name = klass.progid.gsub(/\./, '_') - end - if /^[A-Z]/ !~ klass_name || Module.constants.include?(klass_name) - klass_name = 'OLE' + klass_name - end - klass_name - end - - def define_initialize(klass) - <<STR - - def initialize(obj = nil) - @clsid = "#{klass.guid}" - @progid = "#{klass.progid}" - if obj.nil? - @dispatch = WIN32OLE.new @progid - else - @dispatch = obj - end - end -STR - end - - def define_include - " include WIN32OLE::VARIANT" - end - - def define_instance_variables - " attr_reader :lastargs" - end - - def define_method_missing - <<STR - - def method_missing(cmd, *arg) - @dispatch.method_missing(cmd, *arg) - end -STR - end - - def define_class(klass, io = STDOUT) - io.puts "class #{class_name(klass)} # #{klass.name}" - io.puts define_include - io.puts define_instance_variables - io.puts " attr_reader :dispatch" - io.puts " attr_reader :clsid" - io.puts " attr_reader :progid" - io.puts define_initialize(klass) - io.puts define_method_missing - end - - def define_module(klass, io = STDOUT) - io.puts "module #{class_name(klass)}" - io.puts define_include - io.puts define_instance_variables - end - - def generate_class(klass, io = STDOUT) - io.puts "\n# #{klass.helpstring}" - if klass.ole_type == "Class" && - klass.guid && - klass.progid - @receiver = "@dispatch." - define_class(klass, io) - else - @receiver = "" - define_module(klass, io) - end - generate_constants(klass, io) - generate_methods(klass, io) - io.puts "end" - end - - def generate(io = STDOUT) - io.puts "require 'win32ole'" - io.puts "require 'win32ole/property'" - - ole_classes(typelib).select{|klass| - klass.visible? && - (klass.ole_type == "Class" || - klass.ole_type == "Interface" || - klass.ole_type == "Dispatch" || - klass.ole_type == "Enum") - }.each do |klass| - generate_class(klass, io) - end - begin - @ole.quit if @ole - rescue - end - end -end - -require 'win32ole' -if __FILE__ == $0 - if ARGV.size == 0 - $stderr.puts "usage: #{$0} Type Library [...]" - exit 1 - end - ARGV.each do |typelib| - comgen = WIN32COMGen.new(typelib) - comgen.generate - end -end diff --git a/ext/win32ole/sample/xml.rb b/ext/win32ole/sample/xml.rb deleted file mode 100644 index 5a239c9336..0000000000 --- a/ext/win32ole/sample/xml.rb +++ /dev/null @@ -1,7307 +0,0 @@ -# frozen_string_literal: false -# -# This file created by olegen.rb as following. -# ruby olegen.rb 'Microsoft XML, version 2.0' > xml.rb -# -require 'win32ole' -require 'win32ole/property' - -# -module IXMLDOMImplementation - include WIN32OLE::VARIANT - attr_reader :lastargs - - # BOOL hasFeature - # BSTR arg0 --- feature [IN] - # BSTR arg1 --- version [IN] - def hasFeature(arg0, arg1) - ret = _invoke(145, [arg0, arg1], [VT_BSTR, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# Core DOM node interface -module IXMLDOMNode - include WIN32OLE::VARIANT - attr_reader :lastargs - - # BSTR nodeName - # name of the node - def nodeName() - ret = _getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = _getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = _getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = _getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = _getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = _getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = _getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = _getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = _getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = _getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = _getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = _getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = _getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = _getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = _getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = _getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = _getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = _getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = _getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = _getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = _getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = _getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = _setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = _setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = _setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = _setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = _invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = _invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = _invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = _invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = _invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = _invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = _invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = _invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = _invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = _invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# Constants that define a node's type -module OLEtagDOMNodeType - include WIN32OLE::VARIANT - attr_reader :lastargs - NODE_INVALID = 0 - NODE_ELEMENT = 1 - NODE_ATTRIBUTE = 2 - NODE_TEXT = 3 - NODE_CDATA_SECTION = 4 - NODE_ENTITY_REFERENCE = 5 - NODE_ENTITY = 6 - NODE_PROCESSING_INSTRUCTION = 7 - NODE_COMMENT = 8 - NODE_DOCUMENT = 9 - NODE_DOCUMENT_TYPE = 10 - NODE_DOCUMENT_FRAGMENT = 11 - NODE_NOTATION = 12 -end - -# -module IXMLDOMNodeList - include WIN32OLE::VARIANT - attr_reader :lastargs - - # I4 length - # number of nodes in the collection - def length() - ret = _getproperty(74, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # PTR item - # collection of nodes - # I4 arg0 --- index [IN] - def item - OLEProperty.new(self, 0, [VT_I4], [VT_I4, VT_BYREF|VT_DISPATCH]) - end - - # IXMLDOMNode nextNode - # get next node from iterator - def nextNode() - ret = _invoke(76, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID reset - # reset the position of iterator - def reset() - ret = _invoke(77, [], []) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# -module IXMLDOMNamedNodeMap - include WIN32OLE::VARIANT - attr_reader :lastargs - - # I4 length - # number of nodes in the collection - def length() - ret = _getproperty(74, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # PTR item - # collection of nodes - # I4 arg0 --- index [IN] - def item - OLEProperty.new(self, 0, [VT_I4], [VT_I4, VT_BYREF|VT_DISPATCH]) - end - - # IXMLDOMNode getNamedItem - # lookup item by name - # BSTR arg0 --- name [IN] - def getNamedItem(arg0) - ret = _invoke(83, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode setNamedItem - # set item by name - # IXMLDOMNode arg0 --- newItem [IN] - def setNamedItem(arg0) - ret = _invoke(84, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeNamedItem - # remove item by name - # BSTR arg0 --- name [IN] - def removeNamedItem(arg0) - ret = _invoke(85, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode getQualifiedItem - # lookup the item by name and namespace - # BSTR arg0 --- baseName [IN] - # BSTR arg1 --- namespaceURI [IN] - def getQualifiedItem(arg0, arg1) - ret = _invoke(87, [arg0, arg1], [VT_BSTR, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeQualifiedItem - # remove the item by name and namespace - # BSTR arg0 --- baseName [IN] - # BSTR arg1 --- namespaceURI [IN] - def removeQualifiedItem(arg0, arg1) - ret = _invoke(88, [arg0, arg1], [VT_BSTR, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextNode - # get next node from iterator - def nextNode() - ret = _invoke(89, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID reset - # reset the position of iterator - def reset() - ret = _invoke(90, [], []) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# -module IXMLDOMDocument - include WIN32OLE::VARIANT - attr_reader :lastargs - - # BSTR nodeName - # name of the node - def nodeName() - ret = _getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = _getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = _getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = _getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = _getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = _getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = _getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = _getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = _getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = _getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = _getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = _getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = _getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = _getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = _getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = _getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = _getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = _getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = _getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = _getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = _getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = _getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocumentType doctype - # node corresponding to the DOCTYPE - def doctype() - ret = _getproperty(38, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMImplementation implementation - # info on this DOM implementation - def implementation() - ret = _getproperty(39, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMElement documentElement - # the root of the tree - def documentElement() - ret = _getproperty(40, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 readyState - # get the state of the XML document - def readyState() - ret = _getproperty(-525, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMParseError parseError - # get the last parser error - def parseError() - ret = _getproperty(59, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR url - # get the URL for the loaded XML document - def url() - ret = _getproperty(60, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL async - # flag for asynchronous download - def async() - ret = _getproperty(61, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL validateOnParse - # indicates whether the parser performs validation - def validateOnParse() - ret = _getproperty(65, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL resolveExternals - # indicates whether the parser resolves references to external DTD/Entities/Schema - def resolveExternals() - ret = _getproperty(66, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL preserveWhiteSpace - # indicates whether the parser preserves whitespace - def preserveWhiteSpace() - ret = _getproperty(67, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = _setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = _setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = _setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = _setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID async - # flag for asynchronous download - def async=(arg0) - ret = _setproperty(61, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID validateOnParse - # indicates whether the parser performs validation - def validateOnParse=(arg0) - ret = _setproperty(65, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID resolveExternals - # indicates whether the parser resolves references to external DTD/Entities/Schema - def resolveExternals=(arg0) - ret = _setproperty(66, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID preserveWhiteSpace - # indicates whether the parser preserves whitespace - def preserveWhiteSpace=(arg0) - ret = _setproperty(67, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID onreadystatechange - # register a readystatechange event handler - def onreadystatechange=(arg0) - ret = _setproperty(68, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID ondataavailable - # register an ondataavailable event handler - def ondataavailable=(arg0) - ret = _setproperty(69, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID ontransformnode - # register an ontransformnode event handler - def ontransformnode=(arg0) - ret = _setproperty(70, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = _invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = _invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = _invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = _invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = _invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = _invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = _invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = _invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = _invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = _invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMElement createElement - # create an Element node - # BSTR arg0 --- tagName [IN] - def createElement(arg0) - ret = _invoke(41, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocumentFragment createDocumentFragment - # create a DocumentFragment node - def createDocumentFragment() - ret = _invoke(42, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMText createTextNode - # create a text node - # BSTR arg0 --- data [IN] - def createTextNode(arg0) - ret = _invoke(43, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMComment createComment - # create a comment node - # BSTR arg0 --- data [IN] - def createComment(arg0) - ret = _invoke(44, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMCDATASection createCDATASection - # create a CDATA section node - # BSTR arg0 --- data [IN] - def createCDATASection(arg0) - ret = _invoke(45, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMProcessingInstruction createProcessingInstruction - # create a processing instruction node - # BSTR arg0 --- target [IN] - # BSTR arg1 --- data [IN] - def createProcessingInstruction(arg0, arg1) - ret = _invoke(46, [arg0, arg1], [VT_BSTR, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMAttribute createAttribute - # create an attribute node - # BSTR arg0 --- name [IN] - def createAttribute(arg0) - ret = _invoke(47, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMEntityReference createEntityReference - # create an entity reference node - # BSTR arg0 --- name [IN] - def createEntityReference(arg0) - ret = _invoke(49, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList getElementsByTagName - # build a list of elements by name - # BSTR arg0 --- tagName [IN] - def getElementsByTagName(arg0) - ret = _invoke(50, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode createNode - # create a node of the specified node type and name - # VARIANT arg0 --- type [IN] - # BSTR arg1 --- name [IN] - # BSTR arg2 --- namespaceURI [IN] - def createNode(arg0, arg1, arg2) - ret = _invoke(54, [arg0, arg1, arg2], [VT_VARIANT, VT_BSTR, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nodeFromID - # retrieve node from it's ID - # BSTR arg0 --- idString [IN] - def nodeFromID(arg0) - ret = _invoke(56, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL load - # load document from the specified XML source - # VARIANT arg0 --- xmlSource [IN] - def load(arg0) - ret = _invoke(58, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID abort - # abort an asynchronous download - def abort() - ret = _invoke(62, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL loadXML - # load the document from a string - # BSTR arg0 --- bstrXML [IN] - def loadXML(arg0) - ret = _invoke(63, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID save - # save the document to a specified destination - # VARIANT arg0 --- destination [IN] - def save(arg0) - ret = _invoke(64, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# -module IXMLDOMDocumentType - include WIN32OLE::VARIANT - attr_reader :lastargs - - # BSTR nodeName - # name of the node - def nodeName() - ret = _getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = _getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = _getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = _getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = _getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = _getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = _getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = _getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = _getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = _getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = _getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = _getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = _getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = _getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = _getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = _getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = _getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = _getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = _getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = _getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = _getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = _getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR name - # name of the document type (root of the tree) - def name() - ret = _getproperty(131, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap entities - # a list of entities in the document - def entities() - ret = _getproperty(132, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap notations - # a list of notations in the document - def notations() - ret = _getproperty(133, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = _setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = _setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = _setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = _setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = _invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = _invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = _invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = _invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = _invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = _invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = _invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = _invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = _invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = _invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# -module IXMLDOMElement - include WIN32OLE::VARIANT - attr_reader :lastargs - - # BSTR nodeName - # name of the node - def nodeName() - ret = _getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = _getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = _getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = _getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = _getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = _getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = _getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = _getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = _getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = _getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = _getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = _getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = _getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = _getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = _getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = _getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = _getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = _getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = _getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = _getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = _getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = _getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR tagName - # get the tagName of the element - def tagName() - ret = _getproperty(97, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = _setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = _setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = _setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = _setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = _invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = _invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = _invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = _invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = _invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = _invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = _invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = _invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = _invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = _invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT getAttribute - # look up the string value of an attribute by name - # BSTR arg0 --- name [IN] - def getAttribute(arg0) - ret = _invoke(99, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID setAttribute - # set the string value of an attribute by name - # BSTR arg0 --- name [IN] - # VARIANT arg1 --- value [IN] - def setAttribute(arg0, arg1) - ret = _invoke(100, [arg0, arg1], [VT_BSTR, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID removeAttribute - # remove an attribute by name - # BSTR arg0 --- name [IN] - def removeAttribute(arg0) - ret = _invoke(101, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMAttribute getAttributeNode - # look up the attribute node by name - # BSTR arg0 --- name [IN] - def getAttributeNode(arg0) - ret = _invoke(102, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMAttribute setAttributeNode - # set the specified attribute on the element - # IXMLDOMAttribute arg0 --- DOMAttribute [IN] - def setAttributeNode(arg0) - ret = _invoke(103, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMAttribute removeAttributeNode - # remove the specified attribute - # IXMLDOMAttribute arg0 --- DOMAttribute [IN] - def removeAttributeNode(arg0) - ret = _invoke(104, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList getElementsByTagName - # build a list of elements by name - # BSTR arg0 --- tagName [IN] - def getElementsByTagName(arg0) - ret = _invoke(105, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID normalize - # collapse all adjacent text nodes in sub-tree - def normalize() - ret = _invoke(106, [], []) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# -module IXMLDOMAttribute - include WIN32OLE::VARIANT - attr_reader :lastargs - - # BSTR nodeName - # name of the node - def nodeName() - ret = _getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = _getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = _getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = _getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = _getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = _getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = _getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = _getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = _getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = _getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = _getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = _getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = _getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = _getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = _getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = _getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = _getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = _getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = _getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = _getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = _getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = _getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR name - # get name of the attribute - def name() - ret = _getproperty(118, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT value - # string value of the attribute - def value() - ret = _getproperty(120, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = _setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = _setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = _setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = _setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID value - # string value of the attribute - def value=(arg0) - ret = _setproperty(120, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = _invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = _invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = _invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = _invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = _invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = _invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = _invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = _invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = _invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = _invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# -module IXMLDOMDocumentFragment - include WIN32OLE::VARIANT - attr_reader :lastargs - - # BSTR nodeName - # name of the node - def nodeName() - ret = _getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = _getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = _getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = _getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = _getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = _getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = _getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = _getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = _getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = _getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = _getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = _getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = _getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = _getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = _getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = _getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = _getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = _getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = _getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = _getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = _getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = _getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = _setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = _setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = _setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = _setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = _invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = _invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = _invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = _invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = _invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = _invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = _invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = _invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = _invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = _invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# -module IXMLDOMText - include WIN32OLE::VARIANT - attr_reader :lastargs - - # BSTR nodeName - # name of the node - def nodeName() - ret = _getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = _getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = _getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = _getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = _getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = _getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = _getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = _getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = _getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = _getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = _getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = _getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = _getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = _getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = _getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = _getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = _getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = _getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = _getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = _getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = _getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = _getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR data - # value of the node - def data() - ret = _getproperty(109, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 length - # number of characters in value - def length() - ret = _getproperty(110, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = _setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = _setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = _setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = _setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID data - # value of the node - def data=(arg0) - ret = _setproperty(109, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = _invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = _invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = _invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = _invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = _invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = _invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = _invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = _invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = _invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = _invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR substringData - # retrieve substring of value - # I4 arg0 --- offset [IN] - # I4 arg1 --- count [IN] - def substringData(arg0, arg1) - ret = _invoke(111, [arg0, arg1], [VT_I4, VT_I4]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID appendData - # append string to value - # BSTR arg0 --- data [IN] - def appendData(arg0) - ret = _invoke(112, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID insertData - # insert string into value - # I4 arg0 --- offset [IN] - # BSTR arg1 --- data [IN] - def insertData(arg0, arg1) - ret = _invoke(113, [arg0, arg1], [VT_I4, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID deleteData - # delete string within the value - # I4 arg0 --- offset [IN] - # I4 arg1 --- count [IN] - def deleteData(arg0, arg1) - ret = _invoke(114, [arg0, arg1], [VT_I4, VT_I4]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID replaceData - # replace string within the value - # I4 arg0 --- offset [IN] - # I4 arg1 --- count [IN] - # BSTR arg2 --- data [IN] - def replaceData(arg0, arg1, arg2) - ret = _invoke(115, [arg0, arg1, arg2], [VT_I4, VT_I4, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMText splitText - # split the text node into two text nodes at the position specified - # I4 arg0 --- offset [IN] - def splitText(arg0) - ret = _invoke(123, [arg0], [VT_I4]) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# -module IXMLDOMCharacterData - include WIN32OLE::VARIANT - attr_reader :lastargs - - # BSTR nodeName - # name of the node - def nodeName() - ret = _getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = _getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = _getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = _getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = _getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = _getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = _getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = _getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = _getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = _getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = _getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = _getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = _getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = _getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = _getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = _getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = _getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = _getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = _getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = _getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = _getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = _getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR data - # value of the node - def data() - ret = _getproperty(109, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 length - # number of characters in value - def length() - ret = _getproperty(110, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = _setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = _setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = _setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = _setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID data - # value of the node - def data=(arg0) - ret = _setproperty(109, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = _invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = _invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = _invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = _invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = _invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = _invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = _invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = _invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = _invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = _invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR substringData - # retrieve substring of value - # I4 arg0 --- offset [IN] - # I4 arg1 --- count [IN] - def substringData(arg0, arg1) - ret = _invoke(111, [arg0, arg1], [VT_I4, VT_I4]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID appendData - # append string to value - # BSTR arg0 --- data [IN] - def appendData(arg0) - ret = _invoke(112, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID insertData - # insert string into value - # I4 arg0 --- offset [IN] - # BSTR arg1 --- data [IN] - def insertData(arg0, arg1) - ret = _invoke(113, [arg0, arg1], [VT_I4, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID deleteData - # delete string within the value - # I4 arg0 --- offset [IN] - # I4 arg1 --- count [IN] - def deleteData(arg0, arg1) - ret = _invoke(114, [arg0, arg1], [VT_I4, VT_I4]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID replaceData - # replace string within the value - # I4 arg0 --- offset [IN] - # I4 arg1 --- count [IN] - # BSTR arg2 --- data [IN] - def replaceData(arg0, arg1, arg2) - ret = _invoke(115, [arg0, arg1, arg2], [VT_I4, VT_I4, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# -module IXMLDOMComment - include WIN32OLE::VARIANT - attr_reader :lastargs - - # BSTR nodeName - # name of the node - def nodeName() - ret = _getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = _getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = _getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = _getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = _getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = _getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = _getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = _getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = _getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = _getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = _getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = _getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = _getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = _getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = _getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = _getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = _getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = _getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = _getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = _getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = _getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = _getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR data - # value of the node - def data() - ret = _getproperty(109, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 length - # number of characters in value - def length() - ret = _getproperty(110, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = _setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = _setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = _setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = _setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID data - # value of the node - def data=(arg0) - ret = _setproperty(109, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = _invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = _invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = _invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = _invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = _invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = _invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = _invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = _invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = _invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = _invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR substringData - # retrieve substring of value - # I4 arg0 --- offset [IN] - # I4 arg1 --- count [IN] - def substringData(arg0, arg1) - ret = _invoke(111, [arg0, arg1], [VT_I4, VT_I4]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID appendData - # append string to value - # BSTR arg0 --- data [IN] - def appendData(arg0) - ret = _invoke(112, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID insertData - # insert string into value - # I4 arg0 --- offset [IN] - # BSTR arg1 --- data [IN] - def insertData(arg0, arg1) - ret = _invoke(113, [arg0, arg1], [VT_I4, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID deleteData - # delete string within the value - # I4 arg0 --- offset [IN] - # I4 arg1 --- count [IN] - def deleteData(arg0, arg1) - ret = _invoke(114, [arg0, arg1], [VT_I4, VT_I4]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID replaceData - # replace string within the value - # I4 arg0 --- offset [IN] - # I4 arg1 --- count [IN] - # BSTR arg2 --- data [IN] - def replaceData(arg0, arg1, arg2) - ret = _invoke(115, [arg0, arg1, arg2], [VT_I4, VT_I4, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# -module IXMLDOMCDATASection - include WIN32OLE::VARIANT - attr_reader :lastargs - - # BSTR nodeName - # name of the node - def nodeName() - ret = _getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = _getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = _getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = _getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = _getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = _getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = _getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = _getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = _getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = _getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = _getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = _getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = _getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = _getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = _getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = _getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = _getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = _getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = _getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = _getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = _getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = _getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR data - # value of the node - def data() - ret = _getproperty(109, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 length - # number of characters in value - def length() - ret = _getproperty(110, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = _setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = _setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = _setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = _setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID data - # value of the node - def data=(arg0) - ret = _setproperty(109, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = _invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = _invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = _invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = _invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = _invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = _invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = _invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = _invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = _invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = _invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR substringData - # retrieve substring of value - # I4 arg0 --- offset [IN] - # I4 arg1 --- count [IN] - def substringData(arg0, arg1) - ret = _invoke(111, [arg0, arg1], [VT_I4, VT_I4]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID appendData - # append string to value - # BSTR arg0 --- data [IN] - def appendData(arg0) - ret = _invoke(112, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID insertData - # insert string into value - # I4 arg0 --- offset [IN] - # BSTR arg1 --- data [IN] - def insertData(arg0, arg1) - ret = _invoke(113, [arg0, arg1], [VT_I4, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID deleteData - # delete string within the value - # I4 arg0 --- offset [IN] - # I4 arg1 --- count [IN] - def deleteData(arg0, arg1) - ret = _invoke(114, [arg0, arg1], [VT_I4, VT_I4]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID replaceData - # replace string within the value - # I4 arg0 --- offset [IN] - # I4 arg1 --- count [IN] - # BSTR arg2 --- data [IN] - def replaceData(arg0, arg1, arg2) - ret = _invoke(115, [arg0, arg1, arg2], [VT_I4, VT_I4, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMText splitText - # split the text node into two text nodes at the position specified - # I4 arg0 --- offset [IN] - def splitText(arg0) - ret = _invoke(123, [arg0], [VT_I4]) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# -module IXMLDOMProcessingInstruction - include WIN32OLE::VARIANT - attr_reader :lastargs - - # BSTR nodeName - # name of the node - def nodeName() - ret = _getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = _getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = _getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = _getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = _getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = _getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = _getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = _getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = _getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = _getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = _getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = _getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = _getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = _getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = _getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = _getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = _getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = _getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = _getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = _getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = _getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = _getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR target - # the target - def target() - ret = _getproperty(127, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR data - # the data - def data() - ret = _getproperty(128, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = _setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = _setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = _setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = _setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID data - # the data - def data=(arg0) - ret = _setproperty(128, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = _invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = _invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = _invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = _invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = _invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = _invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = _invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = _invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = _invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = _invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# -module IXMLDOMEntityReference - include WIN32OLE::VARIANT - attr_reader :lastargs - - # BSTR nodeName - # name of the node - def nodeName() - ret = _getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = _getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = _getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = _getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = _getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = _getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = _getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = _getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = _getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = _getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = _getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = _getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = _getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = _getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = _getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = _getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = _getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = _getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = _getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = _getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = _getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = _getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = _setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = _setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = _setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = _setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = _invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = _invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = _invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = _invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = _invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = _invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = _invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = _invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = _invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = _invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# structure for reporting parser errors -module IXMLDOMParseError - include WIN32OLE::VARIANT - attr_reader :lastargs - - # I4 errorCode - # the error code - def errorCode() - ret = _getproperty(0, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR url - # the URL of the XML document containing the error - def url() - ret = _getproperty(179, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR reason - # the cause of the error - def reason() - ret = _getproperty(180, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR srcText - # the data where the error occurred - def srcText() - ret = _getproperty(181, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 line - # the line number in the XML document where the error occurred - def line() - ret = _getproperty(182, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 linepos - # the character position in the line containing the error - def linepos() - ret = _getproperty(183, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 filepos - # the absolute file position in the XML document containing the error - def filepos() - ret = _getproperty(184, [], []) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# -module IXMLDOMNotation - include WIN32OLE::VARIANT - attr_reader :lastargs - - # BSTR nodeName - # name of the node - def nodeName() - ret = _getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = _getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = _getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = _getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = _getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = _getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = _getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = _getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = _getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = _getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = _getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = _getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = _getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = _getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = _getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = _getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = _getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = _getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = _getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = _getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = _getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = _getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT publicId - # the public ID - def publicId() - ret = _getproperty(136, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT systemId - # the system ID - def systemId() - ret = _getproperty(137, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = _setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = _setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = _setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = _setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = _invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = _invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = _invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = _invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = _invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = _invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = _invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = _invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = _invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = _invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# -module IXMLDOMEntity - include WIN32OLE::VARIANT - attr_reader :lastargs - - # BSTR nodeName - # name of the node - def nodeName() - ret = _getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = _getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = _getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = _getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = _getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = _getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = _getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = _getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = _getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = _getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = _getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = _getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = _getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = _getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = _getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = _getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = _getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = _getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = _getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = _getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = _getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = _getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT publicId - # the public ID - def publicId() - ret = _getproperty(140, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT systemId - # the system ID - def systemId() - ret = _getproperty(141, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR notationName - # the name of the notation - def notationName() - ret = _getproperty(142, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = _setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = _setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = _setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = _setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = _invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = _invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = _invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = _invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = _invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = _invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = _invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = _invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = _invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = _invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# XTL runtime object -module IXTLRuntime - include WIN32OLE::VARIANT - attr_reader :lastargs - - # BSTR nodeName - # name of the node - def nodeName() - ret = _getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = _getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = _getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = _getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = _getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = _getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = _getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = _getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = _getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = _getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = _getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = _getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = _getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = _getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = _getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = _getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = _getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = _getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = _getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = _getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = _getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = _getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = _setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = _setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = _setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = _setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = _invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = _invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = _invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = _invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = _invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = _invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = _invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = _invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = _invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = _invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 uniqueID - # IXMLDOMNode arg0 --- pNode [IN] - def uniqueID(arg0) - ret = _invoke(187, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 depth - # IXMLDOMNode arg0 --- pNode [IN] - def depth(arg0) - ret = _invoke(188, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 childNumber - # IXMLDOMNode arg0 --- pNode [IN] - def childNumber(arg0) - ret = _invoke(189, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 ancestorChildNumber - # BSTR arg0 --- bstrNodeName [IN] - # IXMLDOMNode arg1 --- pNode [IN] - def ancestorChildNumber(arg0, arg1) - ret = _invoke(190, [arg0, arg1], [VT_BSTR, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 absoluteChildNumber - # IXMLDOMNode arg0 --- pNode [IN] - def absoluteChildNumber(arg0) - ret = _invoke(191, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR formatIndex - # I4 arg0 --- lIndex [IN] - # BSTR arg1 --- bstrFormat [IN] - def formatIndex(arg0, arg1) - ret = _invoke(192, [arg0, arg1], [VT_I4, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR formatNumber - # R8 arg0 --- dblNumber [IN] - # BSTR arg1 --- bstrFormat [IN] - def formatNumber(arg0, arg1) - ret = _invoke(193, [arg0, arg1], [VT_R8, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR formatDate - # VARIANT arg0 --- varDate [IN] - # BSTR arg1 --- bstrFormat [IN] - # VARIANT arg2 --- varDestLocale [IN] - def formatDate(arg0, arg1, arg2=nil) - ret = _invoke(194, [arg0, arg1, arg2], [VT_VARIANT, VT_BSTR, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR formatTime - # VARIANT arg0 --- varTime [IN] - # BSTR arg1 --- bstrFormat [IN] - # VARIANT arg2 --- varDestLocale [IN] - def formatTime(arg0, arg1, arg2=nil) - ret = _invoke(195, [arg0, arg1, arg2], [VT_VARIANT, VT_BSTR, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# W3C-DOM XML Document -class Microsoft_XMLDOM_1_0 # DOMDocument - include WIN32OLE::VARIANT - attr_reader :lastargs - attr_reader :dispatch - attr_reader :clsid - attr_reader :progid - - def initialize(obj = nil) - @clsid = "{2933BF90-7B36-11D2-B20E-00C04F983E60}" - @progid = "Microsoft.XMLDOM.1.0" - if obj.nil? - @dispatch = WIN32OLE.new(@progid) - else - @dispatch = obj - end - end - - def method_missing(cmd, *arg) - @dispatch.method_missing(cmd, *arg) - end - - # BSTR nodeName - # name of the node - def nodeName() - ret = @dispatch._getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = @dispatch._getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = @dispatch._getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = @dispatch._getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = @dispatch._getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = @dispatch._getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = @dispatch._getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = @dispatch._getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = @dispatch._getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = @dispatch._getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = @dispatch._getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = @dispatch._getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = @dispatch._getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = @dispatch._getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = @dispatch._getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = @dispatch._getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = @dispatch._getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = @dispatch._getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = @dispatch._getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = @dispatch._getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = @dispatch._getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = @dispatch._getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocumentType doctype - # node corresponding to the DOCTYPE - def doctype() - ret = @dispatch._getproperty(38, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMImplementation implementation - # info on this DOM implementation - def implementation() - ret = @dispatch._getproperty(39, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMElement documentElement - # the root of the tree - def documentElement() - ret = @dispatch._getproperty(40, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 readyState - # get the state of the XML document - def readyState() - ret = @dispatch._getproperty(-525, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMParseError parseError - # get the last parser error - def parseError() - ret = @dispatch._getproperty(59, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR url - # get the URL for the loaded XML document - def url() - ret = @dispatch._getproperty(60, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL async - # flag for asynchronous download - def async() - ret = @dispatch._getproperty(61, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL validateOnParse - # indicates whether the parser performs validation - def validateOnParse() - ret = @dispatch._getproperty(65, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL resolveExternals - # indicates whether the parser resolves references to external DTD/Entities/Schema - def resolveExternals() - ret = @dispatch._getproperty(66, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL preserveWhiteSpace - # indicates whether the parser preserves whitespace - def preserveWhiteSpace() - ret = @dispatch._getproperty(67, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = @dispatch._setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = @dispatch._setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = @dispatch._setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = @dispatch._setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID async - # flag for asynchronous download - def async=(arg0) - ret = @dispatch._setproperty(61, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID validateOnParse - # indicates whether the parser performs validation - def validateOnParse=(arg0) - ret = @dispatch._setproperty(65, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID resolveExternals - # indicates whether the parser resolves references to external DTD/Entities/Schema - def resolveExternals=(arg0) - ret = @dispatch._setproperty(66, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID preserveWhiteSpace - # indicates whether the parser preserves whitespace - def preserveWhiteSpace=(arg0) - ret = @dispatch._setproperty(67, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID onreadystatechange - # register a readystatechange event handler - def onreadystatechange=(arg0) - ret = @dispatch._setproperty(68, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID ondataavailable - # register an ondataavailable event handler - def ondataavailable=(arg0) - ret = @dispatch._setproperty(69, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID ontransformnode - # register an ontransformnode event handler - def ontransformnode=(arg0) - ret = @dispatch._setproperty(70, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = @dispatch._invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = @dispatch._invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = @dispatch._invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = @dispatch._invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = @dispatch._invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = @dispatch._invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = @dispatch._invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = @dispatch._invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = @dispatch._invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = @dispatch._invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMElement createElement - # create an Element node - # BSTR arg0 --- tagName [IN] - def createElement(arg0) - ret = @dispatch._invoke(41, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocumentFragment createDocumentFragment - # create a DocumentFragment node - def createDocumentFragment() - ret = @dispatch._invoke(42, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMText createTextNode - # create a text node - # BSTR arg0 --- data [IN] - def createTextNode(arg0) - ret = @dispatch._invoke(43, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMComment createComment - # create a comment node - # BSTR arg0 --- data [IN] - def createComment(arg0) - ret = @dispatch._invoke(44, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMCDATASection createCDATASection - # create a CDATA section node - # BSTR arg0 --- data [IN] - def createCDATASection(arg0) - ret = @dispatch._invoke(45, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMProcessingInstruction createProcessingInstruction - # create a processing instruction node - # BSTR arg0 --- target [IN] - # BSTR arg1 --- data [IN] - def createProcessingInstruction(arg0, arg1) - ret = @dispatch._invoke(46, [arg0, arg1], [VT_BSTR, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMAttribute createAttribute - # create an attribute node - # BSTR arg0 --- name [IN] - def createAttribute(arg0) - ret = @dispatch._invoke(47, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMEntityReference createEntityReference - # create an entity reference node - # BSTR arg0 --- name [IN] - def createEntityReference(arg0) - ret = @dispatch._invoke(49, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList getElementsByTagName - # build a list of elements by name - # BSTR arg0 --- tagName [IN] - def getElementsByTagName(arg0) - ret = @dispatch._invoke(50, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode createNode - # create a node of the specified node type and name - # VARIANT arg0 --- type [IN] - # BSTR arg1 --- name [IN] - # BSTR arg2 --- namespaceURI [IN] - def createNode(arg0, arg1, arg2) - ret = @dispatch._invoke(54, [arg0, arg1, arg2], [VT_VARIANT, VT_BSTR, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nodeFromID - # retrieve node from it's ID - # BSTR arg0 --- idString [IN] - def nodeFromID(arg0) - ret = @dispatch._invoke(56, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL load - # load document from the specified XML source - # VARIANT arg0 --- xmlSource [IN] - def load(arg0) - ret = @dispatch._invoke(58, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID abort - # abort an asynchronous download - def abort() - ret = @dispatch._invoke(62, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL loadXML - # load the document from a string - # BSTR arg0 --- bstrXML [IN] - def loadXML(arg0) - ret = @dispatch._invoke(63, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID save - # save the document to a specified destination - # VARIANT arg0 --- destination [IN] - def save(arg0) - ret = @dispatch._invoke(64, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # HRESULT ondataavailable EVENT in XMLDOMDocumentEvents - def ondataavailable() - ret = @dispatch._invoke(198, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # HRESULT onreadystatechange EVENT in XMLDOMDocumentEvents - def onreadystatechange() - ret = @dispatch._invoke(-609, [], []) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# W3C-DOM XML Document (Apartment) -class Microsoft_FreeThreadedXMLDOM_1_0 # DOMFreeThreadedDocument - include WIN32OLE::VARIANT - attr_reader :lastargs - attr_reader :dispatch - attr_reader :clsid - attr_reader :progid - - def initialize(obj = nil) - @clsid = "{2933BF91-7B36-11D2-B20E-00C04F983E60}" - @progid = "Microsoft.FreeThreadedXMLDOM.1.0" - if obj.nil? - @dispatch = WIN32OLE.new(@progid) - else - @dispatch = obj - end - end - - def method_missing(cmd, *arg) - @dispatch.method_missing(cmd, *arg) - end - - # BSTR nodeName - # name of the node - def nodeName() - ret = @dispatch._getproperty(2, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeValue - # value stored in the node - def nodeValue() - ret = @dispatch._getproperty(3, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DOMNodeType nodeType - # the node's type - def nodeType() - ret = @dispatch._getproperty(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode parentNode - # parent of the node - def parentNode() - ret = @dispatch._getproperty(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList childNodes - # the collection of the node's children - def childNodes() - ret = @dispatch._getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode firstChild - # first child of the node - def firstChild() - ret = @dispatch._getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode lastChild - # first child of the node - def lastChild() - ret = @dispatch._getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode previousSibling - # left sibling of the node - def previousSibling() - ret = @dispatch._getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nextSibling - # right sibling of the node - def nextSibling() - ret = @dispatch._getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNamedNodeMap attributes - # the collection of the node's attributes - def attributes() - ret = @dispatch._getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocument ownerDocument - # document that contains the node - def ownerDocument() - ret = @dispatch._getproperty(18, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR nodeTypeString - # the type of node in string form - def nodeTypeString() - ret = @dispatch._getproperty(21, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR text - # text content of the node and subtree - def text() - ret = @dispatch._getproperty(24, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL specified - # indicates whether node is a default value - def specified() - ret = @dispatch._getproperty(22, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode definition - # pointer to the definition of the node in the DTD or schema - def definition() - ret = @dispatch._getproperty(23, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue() - ret = @dispatch._getproperty(25, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT dataType - # the data type of the node - def dataType() - ret = @dispatch._getproperty(26, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR xml - # return the XML source for the node and each of its descendants - def xml() - ret = @dispatch._getproperty(27, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL parsed - # has sub-tree been completely parsed - def parsed() - ret = @dispatch._getproperty(31, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR namespaceURI - # the URI for the namespace applying to the node - def namespaceURI() - ret = @dispatch._getproperty(32, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR prefix - # the prefix for the namespace applying to the node - def prefix() - ret = @dispatch._getproperty(33, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR baseName - # the base name of the node (nodename with the prefix stripped off) - def baseName() - ret = @dispatch._getproperty(34, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocumentType doctype - # node corresponding to the DOCTYPE - def doctype() - ret = @dispatch._getproperty(38, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMImplementation implementation - # info on this DOM implementation - def implementation() - ret = @dispatch._getproperty(39, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMElement documentElement - # the root of the tree - def documentElement() - ret = @dispatch._getproperty(40, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 readyState - # get the state of the XML document - def readyState() - ret = @dispatch._getproperty(-525, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMParseError parseError - # get the last parser error - def parseError() - ret = @dispatch._getproperty(59, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR url - # get the URL for the loaded XML document - def url() - ret = @dispatch._getproperty(60, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL async - # flag for asynchronous download - def async() - ret = @dispatch._getproperty(61, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL validateOnParse - # indicates whether the parser performs validation - def validateOnParse() - ret = @dispatch._getproperty(65, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL resolveExternals - # indicates whether the parser resolves references to external DTD/Entities/Schema - def resolveExternals() - ret = @dispatch._getproperty(66, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL preserveWhiteSpace - # indicates whether the parser preserves whitespace - def preserveWhiteSpace() - ret = @dispatch._getproperty(67, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeValue - # value stored in the node - def nodeValue=(arg0) - ret = @dispatch._setproperty(3, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID text - # text content of the node and subtree - def text=(arg0) - ret = @dispatch._setproperty(24, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID nodeTypedValue - # get the strongly typed value of the node - def nodeTypedValue=(arg0) - ret = @dispatch._setproperty(25, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID dataType - # the data type of the node - def dataType=(arg0) - ret = @dispatch._setproperty(26, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID async - # flag for asynchronous download - def async=(arg0) - ret = @dispatch._setproperty(61, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID validateOnParse - # indicates whether the parser performs validation - def validateOnParse=(arg0) - ret = @dispatch._setproperty(65, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID resolveExternals - # indicates whether the parser resolves references to external DTD/Entities/Schema - def resolveExternals=(arg0) - ret = @dispatch._setproperty(66, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID preserveWhiteSpace - # indicates whether the parser preserves whitespace - def preserveWhiteSpace=(arg0) - ret = @dispatch._setproperty(67, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID onreadystatechange - # register a readystatechange event handler - def onreadystatechange=(arg0) - ret = @dispatch._setproperty(68, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID ondataavailable - # register an ondataavailable event handler - def ondataavailable=(arg0) - ret = @dispatch._setproperty(69, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID ontransformnode - # register an ontransformnode event handler - def ontransformnode=(arg0) - ret = @dispatch._setproperty(70, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode insertBefore - # insert a child node - # IXMLDOMNode arg0 --- newChild [IN] - # VARIANT arg1 --- refChild [IN] - def insertBefore(arg0, arg1) - ret = @dispatch._invoke(13, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode replaceChild - # replace a child node - # IXMLDOMNode arg0 --- newChild [IN] - # IXMLDOMNode arg1 --- oldChild [IN] - def replaceChild(arg0, arg1) - ret = @dispatch._invoke(14, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode removeChild - # remove a child node - # IXMLDOMNode arg0 --- childNode [IN] - def removeChild(arg0) - ret = @dispatch._invoke(15, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode appendChild - # append a child node - # IXMLDOMNode arg0 --- newChild [IN] - def appendChild(arg0) - ret = @dispatch._invoke(16, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL hasChildNodes - def hasChildNodes() - ret = @dispatch._invoke(17, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode cloneNode - # BOOL arg0 --- deep [IN] - def cloneNode(arg0) - ret = @dispatch._invoke(19, [arg0], [VT_BOOL]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR transformNode - # apply the stylesheet to the subtree - # IXMLDOMNode arg0 --- stylesheet [IN] - def transformNode(arg0) - ret = @dispatch._invoke(28, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList selectNodes - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectNodes(arg0) - ret = @dispatch._invoke(29, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode selectSingleNode - # execute query on the subtree - # BSTR arg0 --- queryString [IN] - def selectSingleNode(arg0) - ret = @dispatch._invoke(30, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID transformNodeToObject - # apply the stylesheet to the subtree, returning the result through a document or a stream - # IXMLDOMNode arg0 --- stylesheet [IN] - # VARIANT arg1 --- outputObject [IN] - def transformNodeToObject(arg0, arg1) - ret = @dispatch._invoke(35, [arg0, arg1], [VT_BYREF|VT_DISPATCH, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMElement createElement - # create an Element node - # BSTR arg0 --- tagName [IN] - def createElement(arg0) - ret = @dispatch._invoke(41, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMDocumentFragment createDocumentFragment - # create a DocumentFragment node - def createDocumentFragment() - ret = @dispatch._invoke(42, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMText createTextNode - # create a text node - # BSTR arg0 --- data [IN] - def createTextNode(arg0) - ret = @dispatch._invoke(43, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMComment createComment - # create a comment node - # BSTR arg0 --- data [IN] - def createComment(arg0) - ret = @dispatch._invoke(44, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMCDATASection createCDATASection - # create a CDATA section node - # BSTR arg0 --- data [IN] - def createCDATASection(arg0) - ret = @dispatch._invoke(45, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMProcessingInstruction createProcessingInstruction - # create a processing instruction node - # BSTR arg0 --- target [IN] - # BSTR arg1 --- data [IN] - def createProcessingInstruction(arg0, arg1) - ret = @dispatch._invoke(46, [arg0, arg1], [VT_BSTR, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMAttribute createAttribute - # create an attribute node - # BSTR arg0 --- name [IN] - def createAttribute(arg0) - ret = @dispatch._invoke(47, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMEntityReference createEntityReference - # create an entity reference node - # BSTR arg0 --- name [IN] - def createEntityReference(arg0) - ret = @dispatch._invoke(49, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNodeList getElementsByTagName - # build a list of elements by name - # BSTR arg0 --- tagName [IN] - def getElementsByTagName(arg0) - ret = @dispatch._invoke(50, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode createNode - # create a node of the specified node type and name - # VARIANT arg0 --- type [IN] - # BSTR arg1 --- name [IN] - # BSTR arg2 --- namespaceURI [IN] - def createNode(arg0, arg1, arg2) - ret = @dispatch._invoke(54, [arg0, arg1, arg2], [VT_VARIANT, VT_BSTR, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # IXMLDOMNode nodeFromID - # retrieve node from it's ID - # BSTR arg0 --- idString [IN] - def nodeFromID(arg0) - ret = @dispatch._invoke(56, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL load - # load document from the specified XML source - # VARIANT arg0 --- xmlSource [IN] - def load(arg0) - ret = @dispatch._invoke(58, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID abort - # abort an asynchronous download - def abort() - ret = @dispatch._invoke(62, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BOOL loadXML - # load the document from a string - # BSTR arg0 --- bstrXML [IN] - def loadXML(arg0) - ret = @dispatch._invoke(63, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID save - # save the document to a specified destination - # VARIANT arg0 --- destination [IN] - def save(arg0) - ret = @dispatch._invoke(64, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # HRESULT ondataavailable EVENT in XMLDOMDocumentEvents - def ondataavailable() - ret = @dispatch._invoke(198, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # HRESULT onreadystatechange EVENT in XMLDOMDocumentEvents - def onreadystatechange() - ret = @dispatch._invoke(-609, [], []) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# IXMLHttpRequest Interface -module IXMLHttpRequest - include WIN32OLE::VARIANT - attr_reader :lastargs - - # I4 status - # Get HTTP status code - def status() - ret = _getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR statusText - # Get HTTP status text - def statusText() - ret = _getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DISPATCH responseXML - # Get response body - def responseXML() - ret = _getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR responseText - # Get response body - def responseText() - ret = _getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT responseBody - # Get response body - def responseBody() - ret = _getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT responseStream - # Get response body - def responseStream() - ret = _getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 readyState - # Get ready state - def readyState() - ret = _getproperty(13, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID onreadystatechange - # Register a complete event handler - def onreadystatechange=(arg0) - ret = _setproperty(14, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID open - # Open HTTP connection - # BSTR arg0 --- bstrMethod [IN] - # BSTR arg1 --- bstrUrl [IN] - # VARIANT arg2 --- varAsync [IN] - # VARIANT arg3 --- bstrUser [IN] - # VARIANT arg4 --- bstrPassword [IN] - def open(arg0, arg1, arg2=nil, arg3=nil, arg4=nil) - ret = _invoke(1, [arg0, arg1, arg2, arg3, arg4], [VT_BSTR, VT_BSTR, VT_VARIANT, VT_VARIANT, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID setRequestHeader - # Add HTTP request header - # BSTR arg0 --- bstrHeader [IN] - # BSTR arg1 --- bstrValue [IN] - def setRequestHeader(arg0, arg1) - ret = _invoke(2, [arg0, arg1], [VT_BSTR, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR getResponseHeader - # Get HTTP response header - # BSTR arg0 --- bstrHeader [IN] - def getResponseHeader(arg0) - ret = _invoke(3, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR getAllResponseHeaders - # Get all HTTP response headers - def getAllResponseHeaders() - ret = _invoke(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID send - # Send HTTP request - # VARIANT arg0 --- varBody [IN] - def send(arg0=nil) - ret = _invoke(5, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID abort - # Abort HTTP request - def abort() - ret = _invoke(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# XML HTTP Request class. -class Microsoft_XMLHTTP_1 # XMLHTTPRequest - include WIN32OLE::VARIANT - attr_reader :lastargs - attr_reader :dispatch - attr_reader :clsid - attr_reader :progid - - def initialize(obj = nil) - @clsid = "{ED8C108E-4349-11D2-91A4-00C04F7969E8}" - @progid = "Microsoft.XMLHTTP.1" - if obj.nil? - @dispatch = WIN32OLE.new(@progid) - else - @dispatch = obj - end - end - - def method_missing(cmd, *arg) - @dispatch.method_missing(cmd, *arg) - end - - # I4 status - # Get HTTP status code - def status() - ret = @dispatch._getproperty(7, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR statusText - # Get HTTP status text - def statusText() - ret = @dispatch._getproperty(8, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # DISPATCH responseXML - # Get response body - def responseXML() - ret = @dispatch._getproperty(9, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR responseText - # Get response body - def responseText() - ret = @dispatch._getproperty(10, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT responseBody - # Get response body - def responseBody() - ret = @dispatch._getproperty(11, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VARIANT responseStream - # Get response body - def responseStream() - ret = @dispatch._getproperty(12, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 readyState - # Get ready state - def readyState() - ret = @dispatch._getproperty(13, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID onreadystatechange - # Register a complete event handler - def onreadystatechange=(arg0) - ret = @dispatch._setproperty(14, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID open - # Open HTTP connection - # BSTR arg0 --- bstrMethod [IN] - # BSTR arg1 --- bstrUrl [IN] - # VARIANT arg2 --- varAsync [IN] - # VARIANT arg3 --- bstrUser [IN] - # VARIANT arg4 --- bstrPassword [IN] - def open(arg0, arg1, arg2=nil, arg3=nil, arg4=nil) - ret = @dispatch._invoke(1, [arg0, arg1, arg2, arg3, arg4], [VT_BSTR, VT_BSTR, VT_VARIANT, VT_VARIANT, VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID setRequestHeader - # Add HTTP request header - # BSTR arg0 --- bstrHeader [IN] - # BSTR arg1 --- bstrValue [IN] - def setRequestHeader(arg0, arg1) - ret = @dispatch._invoke(2, [arg0, arg1], [VT_BSTR, VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR getResponseHeader - # Get HTTP response header - # BSTR arg0 --- bstrHeader [IN] - def getResponseHeader(arg0) - ret = @dispatch._invoke(3, [arg0], [VT_BSTR]) - @lastargs = WIN32OLE::ARGV - ret - end - - # BSTR getAllResponseHeaders - # Get all HTTP response headers - def getAllResponseHeaders() - ret = @dispatch._invoke(4, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID send - # Send HTTP request - # VARIANT arg0 --- varBody [IN] - def send(arg0=nil) - ret = @dispatch._invoke(5, [arg0], [VT_VARIANT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID abort - # Abort HTTP request - def abort() - ret = @dispatch._invoke(6, [], []) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# XML Data Source Object -class Microsoft_XMLDSO_1_0 # XMLDSOControl - include WIN32OLE::VARIANT - attr_reader :lastargs - attr_reader :dispatch - attr_reader :clsid - attr_reader :progid - - def initialize(obj = nil) - @clsid = "{550DDA30-0541-11D2-9CA9-0060B0EC3D39}" - @progid = "Microsoft.XMLDSO.1.0" - if obj.nil? - @dispatch = WIN32OLE.new(@progid) - else - @dispatch = obj - end - end - - def method_missing(cmd, *arg) - @dispatch.method_missing(cmd, *arg) - end - - # IXMLDOMDocument XMLDocument - def XMLDocument() - ret = @dispatch._getproperty(65537, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 JavaDSOCompatible - def JavaDSOCompatible() - ret = @dispatch._getproperty(65538, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # I4 readyState - def readyState() - ret = @dispatch._getproperty(-525, [], []) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID XMLDocument - def XMLDocument=(arg0) - ret = @dispatch._setproperty(65537, [arg0], [VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end - - # VOID JavaDSOCompatible - def JavaDSOCompatible=(arg0) - ret = @dispatch._setproperty(65538, [arg0], [VT_I4]) - @lastargs = WIN32OLE::ARGV - ret - end -end - -# Constants that define types for IXMLElement. -module OLEtagXMLEMEM_TYPE - include WIN32OLE::VARIANT - attr_reader :lastargs - XMLELEMTYPE_ELEMENT = 0 - XMLELEMTYPE_TEXT = 1 - XMLELEMTYPE_COMMENT = 2 - XMLELEMTYPE_DOCUMENT = 3 - XMLELEMTYPE_DTD = 4 - XMLELEMTYPE_PI = 5 - XMLELEMTYPE_OTHER = 6 -end - -# XMLDocument extends IXML Document. It is obsolete. You should use DOMDocument. This object should not be confused with the XMLDocument property on the XML data island. -class Msxml # XMLDocument - include WIN32OLE::VARIANT - attr_reader :lastargs - attr_reader :dispatch - attr_reader :clsid - attr_reader :progid - - def initialize(obj = nil) - @clsid = "{CFC399AF-D876-11D0-9C10-00C04FC99C8E}" - @progid = "Msxml" - if obj.nil? - @dispatch = WIN32OLE.new(@progid) - else - @dispatch = obj - end - end - - def method_missing(cmd, *arg) - @dispatch.method_missing(cmd, *arg) - end - - # HRESULT url - # set URL to load an XML document from the URL. - # BSTR arg0 --- p [IN] - def url=(arg0) - ret = @dispatch._setproperty(65641, [arg0], [VT_BSTR, VT_HRESULT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # HRESULT charset - # get encoding. - # BSTR arg0 --- p [IN] - def charset=(arg0) - ret = @dispatch._setproperty(65645, [arg0], [VT_BSTR, VT_HRESULT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # HRESULT async - # get asynchronous loading flag. - # BOOL arg0 --- pf [IN] - def async=(arg0) - ret = @dispatch._setproperty(65649, [arg0], [VT_BOOL, VT_HRESULT]) - @lastargs = WIN32OLE::ARGV - ret - end - - # HRESULT root - # get root IXMLElement of the XML document. - # IXMLElement2,IXMLElement2 arg0 --- p [OUT] - def root - OLEProperty.new(@dispatch, 65637, [VT_BYREF|VT_BYREF|VT_DISPATCH], [VT_BYREF|VT_BYREF|VT_DISPATCH, VT_HRESULT]) - end - - # HRESULT url - # set URL to load an XML document from the URL. - # BSTR arg0 --- p [OUT] - def url - OLEProperty.new(@dispatch, 65641, [VT_BYREF|VT_BSTR], [VT_BYREF|VT_BSTR, VT_HRESULT]) - end - - # HRESULT readyState - # get ready state. - # I4 arg0 --- pl [OUT] - def readyState - OLEProperty.new(@dispatch, 65643, [VT_BYREF|VT_I4], [VT_BYREF|VT_I4, VT_HRESULT]) - end - - # HRESULT charset - # get encoding. - # BSTR arg0 --- p [OUT] - def charset - OLEProperty.new(@dispatch, 65645, [VT_BYREF|VT_BSTR], [VT_BYREF|VT_BSTR, VT_HRESULT]) - end - - # HRESULT version - # get XML version number. - # BSTR arg0 --- p [OUT] - def version - OLEProperty.new(@dispatch, 65646, [VT_BYREF|VT_BSTR], [VT_BYREF|VT_BSTR, VT_HRESULT]) - end - - # HRESULT doctype - # get document type. - # BSTR arg0 --- p [OUT] - def doctype - OLEProperty.new(@dispatch, 65647, [VT_BYREF|VT_BSTR], [VT_BYREF|VT_BSTR, VT_HRESULT]) - end - - # HRESULT async - # get asynchronous loading flag. - # BOOL arg0 --- pf [OUT] - def async - OLEProperty.new(@dispatch, 65649, [VT_BYREF|VT_BOOL], [VT_BYREF|VT_BOOL, VT_HRESULT]) - end - - # HRESULT createElement - # create different types of IXMLElements. - # VARIANT arg0 --- vType [IN] - # VARIANT arg1 --- var1 [IN] - # IXMLElement2,IXMLElement2 arg2 --- ppElem [OUT] - def createElement(arg0, arg1=nil, arg2=nil) - ret = @dispatch._invoke(65644, [arg0, arg1, arg2], [VT_VARIANT, VT_VARIANT, VT_BYREF|VT_BYREF|VT_DISPATCH]) - @lastargs = WIN32OLE::ARGV - ret - end -end diff --git a/ext/win32ole/win32ole.c b/ext/win32ole/win32ole.c deleted file mode 100644 index a060165a1c..0000000000 --- a/ext/win32ole/win32ole.c +++ /dev/null @@ -1,4142 +0,0 @@ -/* - * (c) 1995 Microsoft Corporation. All rights reserved. - * Developed by ActiveWare Internet Corp., now known as - * ActiveState Tool Corp., http://www.ActiveState.com - * - * Other modifications Copyright (c) 1997, 1998 by Gurusamy Sarathy - * <gsar@umich.edu> and Jan Dubois <jan.dubois@ibm.net> - * - * You may distribute under the terms of either the GNU General Public - * License or the Artistic License, as specified in the README file - * of the Perl distribution. - * - */ - -/* - modified for win32ole (ruby) by Masaki.Suketa <masaki.suketa@nifty.ne.jp> - */ - -#include "win32ole.h" - -/* - * unfortunately IID_IMultiLanguage2 is not included in any libXXX.a - * in Cygwin(mingw32). - */ -#if defined(__CYGWIN__) || defined(__MINGW32__) -#undef IID_IMultiLanguage2 -const IID IID_IMultiLanguage2 = {0xDCCFC164, 0x2B38, 0x11d2, {0xB7, 0xEC, 0x00, 0xC0, 0x4F, 0x8F, 0x5D, 0x9A}}; -#endif - -#define WIN32OLE_VERSION "1.8.10" - -typedef HRESULT (STDAPICALLTYPE FNCOCREATEINSTANCEEX) - (REFCLSID, IUnknown*, DWORD, COSERVERINFO*, DWORD, MULTI_QI*); - -typedef HWND (WINAPI FNHTMLHELP)(HWND hwndCaller, LPCSTR pszFile, - UINT uCommand, DWORD dwData); -typedef BOOL (FNENUMSYSEMCODEPAGES) (CODEPAGE_ENUMPROC, DWORD); -VALUE cWIN32OLE; - -#if defined(RB_THREAD_SPECIFIC) && (defined(__CYGWIN__)) -static RB_THREAD_SPECIFIC BOOL g_ole_initialized; -# define g_ole_initialized_init() ((void)0) -# define g_ole_initialized_set(val) (g_ole_initialized = (val)) -#else -static volatile DWORD g_ole_initialized_key = TLS_OUT_OF_INDEXES; -# define g_ole_initialized (TlsGetValue(g_ole_initialized_key)!=0) -# define g_ole_initialized_init() (g_ole_initialized_key = TlsAlloc()) -# define g_ole_initialized_set(val) TlsSetValue(g_ole_initialized_key, (void*)(val)) -#endif - -static BOOL g_uninitialize_hooked = FALSE; -static BOOL g_cp_installed = FALSE; -static BOOL g_lcid_installed = FALSE; -static BOOL g_running_nano = FALSE; -static HINSTANCE ghhctrl = NULL; -static HINSTANCE gole32 = NULL; -static FNCOCREATEINSTANCEEX *gCoCreateInstanceEx = NULL; -static VALUE com_hash; -static VALUE enc2cp_hash; -static IDispatchVtbl com_vtbl; -static UINT cWIN32OLE_cp = CP_ACP; -static rb_encoding *cWIN32OLE_enc; -static UINT g_cp_to_check = CP_ACP; -static char g_lcid_to_check[8 + 1]; -static VARTYPE g_nil_to = VT_ERROR; -static IMessageFilterVtbl message_filter; -static IMessageFilter imessage_filter = { &message_filter }; -static IMessageFilter* previous_filter; - -#if defined(HAVE_TYPE_IMULTILANGUAGE2) -static IMultiLanguage2 *pIMultiLanguage = NULL; -#elif defined(HAVE_TYPE_IMULTILANGUAGE) -static IMultiLanguage *pIMultiLanguage = NULL; -#else -#define pIMultiLanguage NULL /* dummy */ -#endif - -struct oleparam { - DISPPARAMS dp; - OLECHAR** pNamedArgs; -}; - -static HRESULT ( STDMETHODCALLTYPE QueryInterface )(IDispatch __RPC_FAR *, REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject); -static ULONG ( STDMETHODCALLTYPE AddRef )(IDispatch __RPC_FAR * This); -static ULONG ( STDMETHODCALLTYPE Release )(IDispatch __RPC_FAR * This); -static HRESULT ( STDMETHODCALLTYPE GetTypeInfoCount )(IDispatch __RPC_FAR * This, UINT __RPC_FAR *pctinfo); -static HRESULT ( STDMETHODCALLTYPE GetTypeInfo )(IDispatch __RPC_FAR * This, UINT iTInfo, LCID lcid, ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo); -static HRESULT ( STDMETHODCALLTYPE GetIDsOfNames )(IDispatch __RPC_FAR * This, REFIID riid, LPOLESTR __RPC_FAR *rgszNames, UINT cNames, LCID lcid, DISPID __RPC_FAR *rgDispId); -static HRESULT ( STDMETHODCALLTYPE Invoke )( IDispatch __RPC_FAR * This, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS __RPC_FAR *pDispParams, VARIANT __RPC_FAR *pVarResult, EXCEPINFO __RPC_FAR *pExcepInfo, UINT __RPC_FAR *puArgErr); -static IDispatch* val2dispatch(VALUE val); -static double rbtime2vtdate(VALUE tmobj); -static VALUE vtdate2rbtime(double date); -static rb_encoding *ole_cp2encoding(UINT cp); -static UINT ole_encoding2cp(rb_encoding *enc); -NORETURN(static void failed_load_conv51932(void)); -#ifndef pIMultiLanguage -static void load_conv_function51932(void); -#endif -static UINT ole_init_cp(void); -static void ole_freeexceptinfo(EXCEPINFO *pExInfo); -static VALUE ole_excepinfo2msg(EXCEPINFO *pExInfo); -static void ole_free(void *ptr); -static size_t ole_size(const void *ptr); -static LPWSTR ole_mb2wc(char *pm, int len, UINT cp); -static VALUE ole_ary_m_entry(VALUE val, LONG *pid); -static VALUE is_all_index_under(LONG *pid, long *pub, long dim); -static void * get_ptr_of_variant(VARIANT *pvar); -static void ole_set_safe_array(long n, SAFEARRAY *psa, LONG *pid, long *pub, VALUE val, long dim, VARTYPE vt); -static long dimension(VALUE val); -static long ary_len_of_dim(VALUE ary, long dim); -static VALUE ole_set_member(VALUE self, IDispatch *dispatch); -static VALUE fole_s_allocate(VALUE klass); -static VALUE create_win32ole_object(VALUE klass, IDispatch *pDispatch, int argc, VALUE *argv); -static VALUE ary_new_dim(VALUE myary, LONG *pid, LONG *plb, LONG dim); -static void ary_store_dim(VALUE myary, LONG *pid, LONG *plb, LONG dim, VALUE val); -static void ole_const_load(ITypeLib *pTypeLib, VALUE klass, VALUE self); -static HRESULT clsid_from_remote(VALUE host, VALUE com, CLSID *pclsid); -static VALUE ole_create_dcom(VALUE self, VALUE ole, VALUE host, VALUE others); -static VALUE ole_bind_obj(VALUE moniker, int argc, VALUE *argv, VALUE self); -static VALUE fole_s_connect(int argc, VALUE *argv, VALUE self); -static VALUE fole_s_const_load(int argc, VALUE *argv, VALUE self); -static ULONG reference_count(struct oledata * pole); -static VALUE fole_s_reference_count(VALUE self, VALUE obj); -static VALUE fole_s_free(VALUE self, VALUE obj); -static HWND ole_show_help(VALUE helpfile, VALUE helpcontext); -static VALUE fole_s_show_help(int argc, VALUE *argv, VALUE self); -static VALUE fole_s_get_code_page(VALUE self); -static BOOL CALLBACK installed_code_page_proc(LPTSTR str); -static BOOL code_page_installed(UINT cp); -static VALUE fole_s_set_code_page(VALUE self, VALUE vcp); -static VALUE fole_s_get_locale(VALUE self); -static BOOL CALLBACK installed_lcid_proc(LPTSTR str); -static BOOL lcid_installed(LCID lcid); -static VALUE fole_s_set_locale(VALUE self, VALUE vlcid); -static VALUE fole_s_create_guid(VALUE self); -static VALUE fole_s_ole_initialize(VALUE self); -static VALUE fole_s_ole_uninitialize(VALUE self); -static VALUE fole_initialize(int argc, VALUE *argv, VALUE self); -static int hash2named_arg(VALUE key, VALUE val, VALUE pop); -static VALUE set_argv(VARIANTARG* realargs, unsigned int beg, unsigned int end); -static VALUE ole_invoke(int argc, VALUE *argv, VALUE self, USHORT wFlags, BOOL is_bracket); -static VALUE fole_invoke(int argc, VALUE *argv, VALUE self); -static VALUE ole_invoke2(VALUE self, VALUE dispid, VALUE args, VALUE types, USHORT dispkind); -static VALUE fole_invoke2(VALUE self, VALUE dispid, VALUE args, VALUE types); -static VALUE fole_getproperty2(VALUE self, VALUE dispid, VALUE args, VALUE types); -static VALUE fole_setproperty2(VALUE self, VALUE dispid, VALUE args, VALUE types); -static VALUE fole_setproperty_with_bracket(int argc, VALUE *argv, VALUE self); -static VALUE fole_setproperty(int argc, VALUE *argv, VALUE self); -static VALUE fole_getproperty_with_bracket(int argc, VALUE *argv, VALUE self); -static VALUE ole_propertyput(VALUE self, VALUE property, VALUE value); -static VALUE fole_free(VALUE self); -static VALUE ole_each_sub(VALUE pEnumV); -static VALUE ole_ienum_free(VALUE pEnumV); -static VALUE fole_each(VALUE self); -static VALUE fole_missing(int argc, VALUE *argv, VALUE self); -static HRESULT typeinfo_from_ole(struct oledata *pole, ITypeInfo **ppti); -static VALUE ole_methods(VALUE self, int mask); -static VALUE fole_methods(VALUE self); -static VALUE fole_get_methods(VALUE self); -static VALUE fole_put_methods(VALUE self); -static VALUE fole_func_methods(VALUE self); -static VALUE fole_type(VALUE self); -static VALUE fole_typelib(VALUE self); -static VALUE fole_query_interface(VALUE self, VALUE str_iid); -static VALUE fole_respond_to(VALUE self, VALUE method); -static VALUE ole_usertype2val(ITypeInfo *pTypeInfo, TYPEDESC *pTypeDesc, VALUE typedetails); -static VALUE ole_ptrtype2val(ITypeInfo *pTypeInfo, TYPEDESC *pTypeDesc, VALUE typedetails); -static VALUE fole_method_help(VALUE self, VALUE cmdname); -static VALUE fole_activex_initialize(VALUE self); - -static void com_hash_free(void *ptr); -static void com_hash_mark(void *ptr); -static size_t com_hash_size(const void *ptr); -static void check_nano_server(void); - -static const rb_data_type_t ole_datatype = { - "win32ole", - {NULL, ole_free, ole_size,}, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY -}; - -static const rb_data_type_t win32ole_hash_datatype = { - "win32ole_hash", - {com_hash_mark, com_hash_free, com_hash_size,}, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY -}; - -static HRESULT (STDMETHODCALLTYPE mf_QueryInterface)( - IMessageFilter __RPC_FAR * This, - /* [in] */ REFIID riid, - /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject) -{ - if (MEMCMP(riid, &IID_IUnknown, GUID, 1) == 0 - || MEMCMP(riid, &IID_IMessageFilter, GUID, 1) == 0) - { - *ppvObject = &message_filter; - return S_OK; - } - return E_NOINTERFACE; -} - -static ULONG (STDMETHODCALLTYPE mf_AddRef)( - IMessageFilter __RPC_FAR * This) -{ - return 1; -} - -static ULONG (STDMETHODCALLTYPE mf_Release)( - IMessageFilter __RPC_FAR * This) -{ - return 1; -} - -static DWORD (STDMETHODCALLTYPE mf_HandleInComingCall)( - IMessageFilter __RPC_FAR * pThis, - DWORD dwCallType, //Type of incoming call - HTASK threadIDCaller, //Task handle calling this task - DWORD dwTickCount, //Elapsed tick count - LPINTERFACEINFO lpInterfaceInfo //Pointer to INTERFACEINFO structure - ) -{ -#ifdef DEBUG_MESSAGEFILTER - printf("incoming %08X, %08X, %d\n", dwCallType, threadIDCaller, dwTickCount); - fflush(stdout); -#endif - switch (dwCallType) - { - case CALLTYPE_ASYNC: - case CALLTYPE_TOPLEVEL_CALLPENDING: - case CALLTYPE_ASYNC_CALLPENDING: - if (rb_during_gc()) { - return SERVERCALL_RETRYLATER; - } - break; - default: - break; - } - if (previous_filter) { - return previous_filter->lpVtbl->HandleInComingCall(previous_filter, - dwCallType, - threadIDCaller, - dwTickCount, - lpInterfaceInfo); - } - return SERVERCALL_ISHANDLED; -} - -static DWORD (STDMETHODCALLTYPE mf_RetryRejectedCall)( - IMessageFilter* pThis, - HTASK threadIDCallee, //Server task handle - DWORD dwTickCount, //Elapsed tick count - DWORD dwRejectType //Returned rejection message - ) -{ - if (previous_filter) { - return previous_filter->lpVtbl->RetryRejectedCall(previous_filter, - threadIDCallee, - dwTickCount, - dwRejectType); - } - return 1000; -} - -static DWORD (STDMETHODCALLTYPE mf_MessagePending)( - IMessageFilter* pThis, - HTASK threadIDCallee, //Called applications task handle - DWORD dwTickCount, //Elapsed tick count - DWORD dwPendingType //Call type - ) -{ - if (rb_during_gc()) { - return PENDINGMSG_WAITNOPROCESS; - } - if (previous_filter) { - return previous_filter->lpVtbl->MessagePending(previous_filter, - threadIDCallee, - dwTickCount, - dwPendingType); - } - return PENDINGMSG_WAITNOPROCESS; -} - -typedef struct _Win32OLEIDispatch -{ - IDispatch dispatch; - ULONG refcount; - VALUE obj; -} Win32OLEIDispatch; - -static HRESULT ( STDMETHODCALLTYPE QueryInterface )( - IDispatch __RPC_FAR * This, - /* [in] */ REFIID riid, - /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject) -{ - if (MEMCMP(riid, &IID_IUnknown, GUID, 1) == 0 - || MEMCMP(riid, &IID_IDispatch, GUID, 1) == 0) - { - Win32OLEIDispatch* p = (Win32OLEIDispatch*)This; - p->refcount++; - *ppvObject = This; - return S_OK; - } - return E_NOINTERFACE; -} - -static ULONG ( STDMETHODCALLTYPE AddRef )( - IDispatch __RPC_FAR * This) -{ - Win32OLEIDispatch* p = (Win32OLEIDispatch*)This; - return ++(p->refcount); -} - -static ULONG ( STDMETHODCALLTYPE Release )( - IDispatch __RPC_FAR * This) -{ - Win32OLEIDispatch* p = (Win32OLEIDispatch*)This; - ULONG u = --(p->refcount); - if (u == 0) { - st_data_t key = p->obj; - st_delete(DATA_PTR(com_hash), &key, 0); - free(p); - } - return u; -} - -static HRESULT ( STDMETHODCALLTYPE GetTypeInfoCount )( - IDispatch __RPC_FAR * This, - /* [out] */ UINT __RPC_FAR *pctinfo) -{ - return E_NOTIMPL; -} - -static HRESULT ( STDMETHODCALLTYPE GetTypeInfo )( - IDispatch __RPC_FAR * This, - /* [in] */ UINT iTInfo, - /* [in] */ LCID lcid, - /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo) -{ - return E_NOTIMPL; -} - - -static HRESULT ( STDMETHODCALLTYPE GetIDsOfNames )( - IDispatch __RPC_FAR * This, - /* [in] */ REFIID riid, - /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames, - /* [in] */ UINT cNames, - /* [in] */ LCID lcid, - /* [size_is][out] */ DISPID __RPC_FAR *rgDispId) -{ - /* - Win32OLEIDispatch* p = (Win32OLEIDispatch*)This; - */ - char* psz = ole_wc2mb(*rgszNames); // support only one method - ID nameid = rb_check_id_cstr(psz, (long)strlen(psz), cWIN32OLE_enc); - free(psz); - if ((ID)(DISPID)nameid != nameid) return E_NOINTERFACE; - *rgDispId = (DISPID)nameid; - return S_OK; -} - -static /* [local] */ HRESULT ( STDMETHODCALLTYPE Invoke )( - IDispatch __RPC_FAR * This, - /* [in] */ DISPID dispIdMember, - /* [in] */ REFIID riid, - /* [in] */ LCID lcid, - /* [in] */ WORD wFlags, - /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams, - /* [out] */ VARIANT __RPC_FAR *pVarResult, - /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo, - /* [out] */ UINT __RPC_FAR *puArgErr) -{ - VALUE v; - int i; - int args = pDispParams->cArgs; - Win32OLEIDispatch* p = (Win32OLEIDispatch*)This; - VALUE* parg = ALLOCA_N(VALUE, args); - ID mid = (ID)dispIdMember; - for (i = 0; i < args; i++) { - *(parg + i) = ole_variant2val(&pDispParams->rgvarg[args - i - 1]); - } - if (dispIdMember == DISPID_VALUE) { - if (wFlags == DISPATCH_METHOD) { - mid = rb_intern("call"); - } else if (wFlags & DISPATCH_PROPERTYGET) { - mid = rb_intern("value"); - } - } - v = rb_funcallv(p->obj, mid, args, parg); - ole_val2variant(v, pVarResult); - return S_OK; -} - -BOOL -ole_initialized(void) -{ - return g_ole_initialized; -} - -static IDispatch* -val2dispatch(VALUE val) -{ - struct st_table *tbl = DATA_PTR(com_hash); - Win32OLEIDispatch* pdisp; - st_data_t data; - if (st_lookup(tbl, val, &data)) { - pdisp = (Win32OLEIDispatch *)(data & ~FIXNUM_FLAG); - pdisp->refcount++; - } - else { - pdisp = ALLOC(Win32OLEIDispatch); - pdisp->dispatch.lpVtbl = &com_vtbl; - pdisp->refcount = 1; - pdisp->obj = val; - st_insert(tbl, val, (VALUE)pdisp | FIXNUM_FLAG); - } - return &pdisp->dispatch; -} - -static double -rbtime2vtdate(VALUE tmobj) -{ - SYSTEMTIME st; - double t; - double nsec; - - st.wYear = RB_FIX2INT(rb_funcall(tmobj, rb_intern("year"), 0)); - st.wMonth = RB_FIX2INT(rb_funcall(tmobj, rb_intern("month"), 0)); - st.wDay = RB_FIX2INT(rb_funcall(tmobj, rb_intern("mday"), 0)); - st.wHour = RB_FIX2INT(rb_funcall(tmobj, rb_intern("hour"), 0)); - st.wMinute = RB_FIX2INT(rb_funcall(tmobj, rb_intern("min"), 0)); - st.wSecond = RB_FIX2INT(rb_funcall(tmobj, rb_intern("sec"), 0)); - st.wMilliseconds = 0; - SystemTimeToVariantTime(&st, &t); - - /* - * Unfortunately SystemTimeToVariantTime function always ignores the - * wMilliseconds of SYSTEMTIME struct. - * So, we need to calculate milliseconds by ourselves. - */ - nsec = RB_FIX2INT(rb_funcall(tmobj, rb_intern("nsec"), 0)); - nsec /= 1000000.0; - nsec /= (24.0 * 3600.0); - nsec /= 1000; - return t + nsec; -} - -static VALUE -vtdate2rbtime(double date) -{ - SYSTEMTIME st; - VALUE v; - double msec; - double sec; - VariantTimeToSystemTime(date, &st); - v = rb_funcall(rb_cTime, rb_intern("new"), 6, - RB_INT2FIX(st.wYear), - RB_INT2FIX(st.wMonth), - RB_INT2FIX(st.wDay), - RB_INT2FIX(st.wHour), - RB_INT2FIX(st.wMinute), - RB_INT2FIX(st.wSecond)); - st.wYear = RB_FIX2INT(rb_funcall(v, rb_intern("year"), 0)); - st.wMonth = RB_FIX2INT(rb_funcall(v, rb_intern("month"), 0)); - st.wDay = RB_FIX2INT(rb_funcall(v, rb_intern("mday"), 0)); - st.wHour = RB_FIX2INT(rb_funcall(v, rb_intern("hour"), 0)); - st.wMinute = RB_FIX2INT(rb_funcall(v, rb_intern("min"), 0)); - st.wSecond = RB_FIX2INT(rb_funcall(v, rb_intern("sec"), 0)); - st.wMilliseconds = 0; - SystemTimeToVariantTime(&st, &sec); - /* - * Unfortunately VariantTimeToSystemTime always ignores the - * wMilliseconds of SYSTEMTIME struct(The wMilliseconds is 0). - * So, we need to calculate milliseconds by ourselves. - */ - msec = date - sec; - msec *= 24 * 60; - msec -= floor(msec); - msec *= 60; - if (msec >= 59) { - msec -= 60; - } - if (msec != 0) { - return rb_funcall(v, rb_intern("+"), 1, rb_float_new(msec)); - } - return v; -} - -#define ENC_MACHING_CP(enc,encname,cp) if(strcasecmp(rb_enc_name((enc)),(encname)) == 0) return cp - -static UINT ole_encoding2cp(rb_encoding *enc) -{ - /* - * Is there any better solution to convert - * Ruby encoding to Windows codepage??? - */ - ENC_MACHING_CP(enc, "Big5", 950); - ENC_MACHING_CP(enc, "CP51932", 51932); - ENC_MACHING_CP(enc, "CP850", 850); - ENC_MACHING_CP(enc, "CP852", 852); - ENC_MACHING_CP(enc, "CP855", 855); - ENC_MACHING_CP(enc, "CP949", 949); - ENC_MACHING_CP(enc, "EUC-JP", 20932); - ENC_MACHING_CP(enc, "EUC-KR", 51949); - ENC_MACHING_CP(enc, "EUC-TW", 51950); - ENC_MACHING_CP(enc, "GB18030", 54936); - ENC_MACHING_CP(enc, "GB2312", 20936); - ENC_MACHING_CP(enc, "GBK", 936); - ENC_MACHING_CP(enc, "IBM437", 437); - ENC_MACHING_CP(enc, "IBM720", 720); - ENC_MACHING_CP(enc, "IBM737", 737); - ENC_MACHING_CP(enc, "IBM775", 775); - ENC_MACHING_CP(enc, "IBM852", 852); - ENC_MACHING_CP(enc, "IBM855", 855); - ENC_MACHING_CP(enc, "IBM857", 857); - ENC_MACHING_CP(enc, "IBM860", 860); - ENC_MACHING_CP(enc, "IBM861", 861); - ENC_MACHING_CP(enc, "IBM862", 862); - ENC_MACHING_CP(enc, "IBM863", 863); - ENC_MACHING_CP(enc, "IBM864", 864); - ENC_MACHING_CP(enc, "IBM865", 865); - ENC_MACHING_CP(enc, "IBM866", 866); - ENC_MACHING_CP(enc, "IBM869", 869); - ENC_MACHING_CP(enc, "ISO-2022-JP", 50220); - ENC_MACHING_CP(enc, "ISO-8859-1", 28591); - ENC_MACHING_CP(enc, "ISO-8859-15", 28605); - ENC_MACHING_CP(enc, "ISO-8859-2", 28592); - ENC_MACHING_CP(enc, "ISO-8859-3", 28593); - ENC_MACHING_CP(enc, "ISO-8859-4", 28594); - ENC_MACHING_CP(enc, "ISO-8859-5", 28595); - ENC_MACHING_CP(enc, "ISO-8859-6", 28596); - ENC_MACHING_CP(enc, "ISO-8859-7", 28597); - ENC_MACHING_CP(enc, "ISO-8859-8", 28598); - ENC_MACHING_CP(enc, "ISO-8859-9", 28599); - ENC_MACHING_CP(enc, "KOI8-R", 20866); - ENC_MACHING_CP(enc, "KOI8-U", 21866); - ENC_MACHING_CP(enc, "Shift_JIS", 932); - ENC_MACHING_CP(enc, "UTF-16BE", 1201); - ENC_MACHING_CP(enc, "UTF-16LE", 1200); - ENC_MACHING_CP(enc, "UTF-7", 65000); - ENC_MACHING_CP(enc, "UTF-8", 65001); - ENC_MACHING_CP(enc, "Windows-1250", 1250); - ENC_MACHING_CP(enc, "Windows-1251", 1251); - ENC_MACHING_CP(enc, "Windows-1252", 1252); - ENC_MACHING_CP(enc, "Windows-1253", 1253); - ENC_MACHING_CP(enc, "Windows-1254", 1254); - ENC_MACHING_CP(enc, "Windows-1255", 1255); - ENC_MACHING_CP(enc, "Windows-1256", 1256); - ENC_MACHING_CP(enc, "Windows-1257", 1257); - ENC_MACHING_CP(enc, "Windows-1258", 1258); - ENC_MACHING_CP(enc, "Windows-31J", 932); - ENC_MACHING_CP(enc, "Windows-874", 874); - ENC_MACHING_CP(enc, "eucJP-ms", 20932); - return CP_ACP; -} - -static void -failed_load_conv51932(void) -{ - rb_raise(eWIN32OLERuntimeError, "fail to load convert function for CP51932"); -} - -#ifndef pIMultiLanguage -static void -load_conv_function51932(void) -{ - HRESULT hr = E_NOINTERFACE; - void *p; - if (!pIMultiLanguage) { -#if defined(HAVE_TYPE_IMULTILANGUAGE2) - hr = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, - &IID_IMultiLanguage2, &p); -#elif defined(HAVE_TYPE_IMULTILANGUAGE) - hr = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, - &IID_IMultiLanguage, &p); -#endif - if (FAILED(hr)) { - failed_load_conv51932(); - } - pIMultiLanguage = p; - } -} -#define need_conv_function51932() (load_conv_function51932(), 1) -#else -#define load_conv_function51932() failed_load_conv51932() -#define need_conv_function51932() (failed_load_conv51932(), 0) -#endif - -#define conv_51932(cp) ((cp) == 51932 && need_conv_function51932()) - -static void -set_ole_codepage(UINT cp) -{ - if (code_page_installed(cp)) { - cWIN32OLE_cp = cp; - } else { - switch(cp) { - case CP_ACP: - case CP_OEMCP: - case CP_MACCP: - case CP_THREAD_ACP: - case CP_SYMBOL: - case CP_UTF7: - case CP_UTF8: - cWIN32OLE_cp = cp; - break; - case 51932: - cWIN32OLE_cp = cp; - load_conv_function51932(); - break; - default: - rb_raise(eWIN32OLERuntimeError, "codepage should be WIN32OLE::CP_ACP, WIN32OLE::CP_OEMCP, WIN32OLE::CP_MACCP, WIN32OLE::CP_THREAD_ACP, WIN32OLE::CP_SYMBOL, WIN32OLE::CP_UTF7, WIN32OLE::CP_UTF8, or installed codepage."); - break; - } - } - cWIN32OLE_enc = ole_cp2encoding(cWIN32OLE_cp); -} - - -static UINT -ole_init_cp(void) -{ - UINT cp; - rb_encoding *encdef; - encdef = rb_default_internal_encoding(); - if (!encdef) { - encdef = rb_default_external_encoding(); - } - cp = ole_encoding2cp(encdef); - set_ole_codepage(cp); - return cp; -} - -struct myCPINFOEX { - UINT MaxCharSize; - BYTE DefaultChar[2]; - BYTE LeadByte[12]; - WCHAR UnicodeDefaultChar; - UINT CodePage; - char CodePageName[MAX_PATH]; -}; - -static rb_encoding * -ole_cp2encoding(UINT cp) -{ - static BOOL (*pGetCPInfoEx)(UINT, DWORD, struct myCPINFOEX *) = NULL; - struct myCPINFOEX* buf; - VALUE enc_name; - char *enc_cstr; - int idx; - - if (!code_page_installed(cp)) { - switch(cp) { - case CP_ACP: - cp = GetACP(); - break; - case CP_OEMCP: - cp = GetOEMCP(); - break; - case CP_MACCP: - case CP_THREAD_ACP: - if (!pGetCPInfoEx) { - pGetCPInfoEx = (BOOL (*)(UINT, DWORD, struct myCPINFOEX *)) - GetProcAddress(GetModuleHandle("kernel32"), "GetCPInfoEx"); - if (!pGetCPInfoEx) { - pGetCPInfoEx = (void*)-1; - } - } - buf = ALLOCA_N(struct myCPINFOEX, 1); - ZeroMemory(buf, sizeof(struct myCPINFOEX)); - if (pGetCPInfoEx == (void*)-1 || !pGetCPInfoEx(cp, 0, buf)) { - rb_raise(eWIN32OLERuntimeError, "cannot map codepage to encoding."); - break; /* never reach here */ - } - cp = buf->CodePage; - break; - case CP_SYMBOL: - case CP_UTF7: - case CP_UTF8: - break; - case 51932: - load_conv_function51932(); - break; - default: - rb_raise(eWIN32OLERuntimeError, "codepage should be WIN32OLE::CP_ACP, WIN32OLE::CP_OEMCP, WIN32OLE::CP_MACCP, WIN32OLE::CP_THREAD_ACP, WIN32OLE::CP_SYMBOL, WIN32OLE::CP_UTF7, WIN32OLE::CP_UTF8, or installed codepage."); - break; - } - } - - enc_name = rb_sprintf("CP%d", cp); - idx = rb_enc_find_index(enc_cstr = StringValueCStr(enc_name)); - if (idx < 0) - idx = rb_define_dummy_encoding(enc_cstr); - return rb_enc_from_index(idx); -} - -#ifndef pIMultiLanguage -static HRESULT -ole_ml_wc2mb_conv0(LPWSTR pw, LPSTR pm, UINT *size) -{ - DWORD dw = 0; - return pIMultiLanguage->lpVtbl->ConvertStringFromUnicode(pIMultiLanguage, - &dw, cWIN32OLE_cp, pw, NULL, pm, size); -} -#define ole_ml_wc2mb_conv(pw, pm, size, onfailure) do { \ - HRESULT hr = ole_ml_wc2mb_conv0(pw, pm, &size); \ - if (FAILED(hr)) { \ - onfailure; \ - ole_raise(hr, eWIN32OLERuntimeError, "fail to convert Unicode to CP%d", cWIN32OLE_cp); \ - } \ - } while (0) -#endif - -#define ole_wc2mb_conv(pw, pm, size) WideCharToMultiByte(cWIN32OLE_cp, 0, (pw), -1, (pm), (size), NULL, NULL) - -static char * -ole_wc2mb_alloc(LPWSTR pw, char *(alloc)(UINT size, void *arg), void *arg) -{ - LPSTR pm; - UINT size = 0; - if (conv_51932(cWIN32OLE_cp)) { -#ifndef pIMultiLanguage - ole_ml_wc2mb_conv(pw, NULL, size, {}); - pm = alloc(size, arg); - if (size) ole_ml_wc2mb_conv(pw, pm, size, xfree(pm)); - pm[size] = '\0'; - return pm; -#endif - } - size = ole_wc2mb_conv(pw, NULL, 0); - pm = alloc(size, arg); - if (size) ole_wc2mb_conv(pw, pm, size); - pm[size] = '\0'; - return pm; -} - -static char * -ole_alloc_str(UINT size, void *arg) -{ - return ALLOC_N(char, size + 1); -} - -char * -ole_wc2mb(LPWSTR pw) -{ - return ole_wc2mb_alloc(pw, ole_alloc_str, NULL); -} - -static void -ole_freeexceptinfo(EXCEPINFO *pExInfo) -{ - SysFreeString(pExInfo->bstrDescription); - SysFreeString(pExInfo->bstrSource); - SysFreeString(pExInfo->bstrHelpFile); -} - -static VALUE -ole_excepinfo2msg(EXCEPINFO *pExInfo) -{ - char error_code[40]; - char *pSource = NULL; - char *pDescription = NULL; - VALUE error_msg; - if(pExInfo->pfnDeferredFillIn != NULL) { - (*pExInfo->pfnDeferredFillIn)(pExInfo); - } - if (pExInfo->bstrSource != NULL) { - pSource = ole_wc2mb(pExInfo->bstrSource); - } - if (pExInfo->bstrDescription != NULL) { - pDescription = ole_wc2mb(pExInfo->bstrDescription); - } - if(pExInfo->wCode == 0) { - sprintf(error_code, "\n OLE error code:%lX in ", (unsigned long)pExInfo->scode); - } - else{ - sprintf(error_code, "\n OLE error code:%u in ", pExInfo->wCode); - } - error_msg = rb_str_new2(error_code); - if(pSource != NULL) { - rb_str_cat2(error_msg, pSource); - } - else { - rb_str_cat(error_msg, "<Unknown>", 9); - } - rb_str_cat2(error_msg, "\n "); - if(pDescription != NULL) { - rb_str_cat2(error_msg, pDescription); - } - else { - rb_str_cat2(error_msg, "<No Description>"); - } - if(pSource) free(pSource); - if(pDescription) free(pDescription); - ole_freeexceptinfo(pExInfo); - return error_msg; -} - -void -ole_uninitialize(void) -{ - if (!g_ole_initialized) return; - OleUninitialize(); - g_ole_initialized_set(FALSE); -} - -static void -ole_uninitialize_hook(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass) -{ - ole_uninitialize(); -} - -void -ole_initialize(void) -{ - HRESULT hr; - - if(!g_uninitialize_hooked) { - rb_add_event_hook(ole_uninitialize_hook, RUBY_EVENT_THREAD_END, Qnil); - g_uninitialize_hooked = TRUE; - } - - if(g_ole_initialized == FALSE) { - if(g_running_nano) { - hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); - } else { - hr = OleInitialize(NULL); - } - if(FAILED(hr)) { - ole_raise(hr, rb_eRuntimeError, "fail: OLE initialize"); - } - g_ole_initialized_set(TRUE); - - if (g_running_nano == FALSE) { - hr = CoRegisterMessageFilter(&imessage_filter, &previous_filter); - if(FAILED(hr)) { - previous_filter = NULL; - ole_raise(hr, rb_eRuntimeError, "fail: install OLE MessageFilter"); - } - } - } -} - -static void -ole_free(void *ptr) -{ - struct oledata *pole = ptr; - OLE_FREE(pole->pDispatch); - free(pole); -} - -static size_t ole_size(const void *ptr) -{ - return ptr ? sizeof(struct oledata) : 0; -} - -struct oledata * -oledata_get_struct(VALUE ole) -{ - struct oledata *pole; - TypedData_Get_Struct(ole, struct oledata, &ole_datatype, pole); - return pole; -} - -LPWSTR -ole_vstr2wc(VALUE vstr) -{ - rb_encoding *enc; - int cp; - LPWSTR pw; - st_data_t data; - struct st_table *tbl = DATA_PTR(enc2cp_hash); - - /* do not type-conversion here to prevent from other arguments - * changing (if exist) */ - Check_Type(vstr, T_STRING); - if (RSTRING_LEN(vstr) == 0) { - return NULL; - } - - enc = rb_enc_get(vstr); - - if (st_lookup(tbl, (VALUE)enc | FIXNUM_FLAG, &data)) { - cp = RB_FIX2INT((VALUE)data); - } else { - cp = ole_encoding2cp(enc); - if (code_page_installed(cp) || - cp == CP_ACP || - cp == CP_OEMCP || - cp == CP_MACCP || - cp == CP_THREAD_ACP || - cp == CP_SYMBOL || - cp == CP_UTF7 || - cp == CP_UTF8 || - cp == 51932) { - st_insert(tbl, (VALUE)enc | FIXNUM_FLAG, RB_INT2FIX(cp)); - } else { - rb_raise(eWIN32OLERuntimeError, "not installed Windows codepage(%d) according to `%s'", cp, rb_enc_name(enc)); - } - } - pw = ole_mb2wc(RSTRING_PTR(vstr), RSTRING_LENINT(vstr), cp); - RB_GC_GUARD(vstr); - return pw; -} - -static LPWSTR -ole_mb2wc(char *pm, int len, UINT cp) -{ - UINT size = 0; - LPWSTR pw; - - if (conv_51932(cp)) { -#ifndef pIMultiLanguage - DWORD dw = 0; - UINT n = len; - HRESULT hr = pIMultiLanguage->lpVtbl->ConvertStringToUnicode(pIMultiLanguage, - &dw, cp, pm, &n, NULL, &size); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, "fail to convert CP%d to Unicode", cp); - } - pw = SysAllocStringLen(NULL, size); - n = len; - hr = pIMultiLanguage->lpVtbl->ConvertStringToUnicode(pIMultiLanguage, - &dw, cp, pm, &n, pw, &size); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, "fail to convert CP%d to Unicode", cp); - } - return pw; -#endif - } - size = MultiByteToWideChar(cp, 0, pm, len, NULL, 0); - pw = SysAllocStringLen(NULL, size); - pw[size-1] = 0; - MultiByteToWideChar(cp, 0, pm, len, pw, size); - return pw; -} - -static char * -ole_alloc_vstr(UINT size, void *arg) -{ - VALUE str = rb_enc_str_new(NULL, size, cWIN32OLE_enc); - *(VALUE *)arg = str; - return RSTRING_PTR(str); -} - -VALUE -ole_wc2vstr(LPWSTR pw, BOOL isfree) -{ - VALUE vstr; - ole_wc2mb_alloc(pw, ole_alloc_vstr, &vstr); - rb_str_set_len(vstr, (long)strlen(RSTRING_PTR(vstr))); - if(isfree) - SysFreeString(pw); - return vstr; -} - -static VALUE -ole_ary_m_entry(VALUE val, LONG *pid) -{ - VALUE obj = Qnil; - int i = 0; - obj = val; - while(RB_TYPE_P(obj, T_ARRAY)) { - obj = rb_ary_entry(obj, pid[i]); - i++; - } - return obj; -} - -static VALUE -is_all_index_under(LONG *pid, long *pub, long dim) -{ - long i = 0; - for (i = 0; i < dim; i++) { - if (pid[i] > pub[i]) { - return Qfalse; - } - } - return Qtrue; -} - -void -ole_val2variant_ex(VALUE val, VARIANT *var, VARTYPE vt) -{ - if (val == Qnil) { - if (vt == VT_VARIANT) { - ole_val2variant2(val, var); - } else { - V_VT(var) = (vt & ~VT_BYREF); - if (V_VT(var) == VT_DISPATCH) { - V_DISPATCH(var) = NULL; - } else if (V_VT(var) == VT_UNKNOWN) { - V_UNKNOWN(var) = NULL; - } - } - return; - } -#if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) - switch(vt & ~VT_BYREF) { - case VT_I8: - V_VT(var) = VT_I8; - V_I8(var) = NUM2I8 (val); - break; - case VT_UI8: - V_VT(var) = VT_UI8; - V_UI8(var) = NUM2UI8(val); - break; - default: - ole_val2variant2(val, var); - break; - } -#else /* (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) */ - ole_val2variant2(val, var); -#endif -} - -VOID * -val2variant_ptr(VALUE val, VARIANT *var, VARTYPE vt) -{ - VOID *p = NULL; - HRESULT hr = S_OK; - ole_val2variant_ex(val, var, vt); - if ((vt & ~VT_BYREF) == VT_VARIANT) { - p = var; - } else { - if ( (vt & ~VT_BYREF) != V_VT(var)) { - hr = VariantChangeTypeEx(var, var, - cWIN32OLE_lcid, 0, (VARTYPE)(vt & ~VT_BYREF)); - if (FAILED(hr)) { - ole_raise(hr, rb_eRuntimeError, "failed to change type"); - } - } - p = get_ptr_of_variant(var); - } - if (p == NULL) { - rb_raise(rb_eRuntimeError, "failed to get pointer of variant"); - } - return p; -} - -static void * -get_ptr_of_variant(VARIANT *pvar) -{ - switch(V_VT(pvar)) { - case VT_UI1: - return &V_UI1(pvar); - break; - case VT_I2: - return &V_I2(pvar); - break; - case VT_UI2: - return &V_UI2(pvar); - break; - case VT_I4: - return &V_I4(pvar); - break; - case VT_UI4: - return &V_UI4(pvar); - break; - case VT_R4: - return &V_R4(pvar); - break; - case VT_R8: - return &V_R8(pvar); - break; -#if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) - case VT_I8: - return &V_I8(pvar); - break; - case VT_UI8: - return &V_UI8(pvar); - break; -#endif - case VT_INT: - return &V_INT(pvar); - break; - case VT_UINT: - return &V_UINT(pvar); - break; - case VT_CY: - return &V_CY(pvar); - break; - case VT_DATE: - return &V_DATE(pvar); - break; - case VT_BSTR: - return V_BSTR(pvar); - break; - case VT_DISPATCH: - return V_DISPATCH(pvar); - break; - case VT_ERROR: - return &V_ERROR(pvar); - break; - case VT_BOOL: - return &V_BOOL(pvar); - break; - case VT_UNKNOWN: - return V_UNKNOWN(pvar); - break; - case VT_ARRAY: - return &V_ARRAY(pvar); - break; - default: - return NULL; - break; - } -} - -static void -ole_set_safe_array(long n, SAFEARRAY *psa, LONG *pid, long *pub, VALUE val, long dim, VARTYPE vt) -{ - VALUE val1; - HRESULT hr = S_OK; - VARIANT var; - VOID *p = NULL; - long i = n; - while(i >= 0) { - val1 = ole_ary_m_entry(val, pid); - VariantInit(&var); - p = val2variant_ptr(val1, &var, vt); - if (is_all_index_under(pid, pub, dim) == Qtrue) { - if ((V_VT(&var) == VT_DISPATCH && V_DISPATCH(&var) == NULL) || - (V_VT(&var) == VT_UNKNOWN && V_UNKNOWN(&var) == NULL)) { - rb_raise(eWIN32OLERuntimeError, "element of array does not have IDispatch or IUnknown Interface"); - } - hr = SafeArrayPutElement(psa, pid, p); - } - if (FAILED(hr)) { - ole_raise(hr, rb_eRuntimeError, "failed to SafeArrayPutElement"); - } - pid[i] += 1; - if (pid[i] > pub[i]) { - pid[i] = 0; - i -= 1; - } else { - i = dim - 1; - } - } -} - -static long -dimension(VALUE val) { - long dim = 0; - long dim1 = 0; - long len = 0; - long i = 0; - if (RB_TYPE_P(val, T_ARRAY)) { - len = RARRAY_LEN(val); - for (i = 0; i < len; i++) { - dim1 = dimension(rb_ary_entry(val, i)); - if (dim < dim1) { - dim = dim1; - } - } - dim += 1; - } - return dim; -} - -static long -ary_len_of_dim(VALUE ary, long dim) { - long ary_len = 0; - long ary_len1 = 0; - long len = 0; - long i = 0; - VALUE val; - if (dim == 0) { - if (RB_TYPE_P(ary, T_ARRAY)) { - ary_len = RARRAY_LEN(ary); - } - } else { - if (RB_TYPE_P(ary, T_ARRAY)) { - len = RARRAY_LEN(ary); - for (i = 0; i < len; i++) { - val = rb_ary_entry(ary, i); - ary_len1 = ary_len_of_dim(val, dim-1); - if (ary_len < ary_len1) { - ary_len = ary_len1; - } - } - } - } - return ary_len; -} - -HRESULT -ole_val_ary2variant_ary(VALUE val, VARIANT *var, VARTYPE vt) -{ - long dim = 0; - int i = 0; - HRESULT hr = S_OK; - - SAFEARRAYBOUND *psab = NULL; - SAFEARRAY *psa = NULL; - long *pub; - LONG *pid; - - Check_Type(val, T_ARRAY); - - dim = dimension(val); - - psab = ALLOC_N(SAFEARRAYBOUND, dim); - pub = ALLOC_N(long, dim); - pid = ALLOC_N(LONG, dim); - - if(!psab || !pub || !pid) { - if(pub) free(pub); - if(psab) free(psab); - if(pid) free(pid); - rb_raise(rb_eRuntimeError, "memory allocation error"); - } - - for (i = 0; i < dim; i++) { - psab[i].cElements = ary_len_of_dim(val, i); - psab[i].lLbound = 0; - pub[i] = psab[i].cElements - 1; - pid[i] = 0; - } - /* Create and fill VARIANT array */ - if ((vt & ~VT_BYREF) == VT_ARRAY) { - vt = (vt | VT_VARIANT); - } - psa = SafeArrayCreate((VARTYPE)(vt & VT_TYPEMASK), dim, psab); - if (psa == NULL) - hr = E_OUTOFMEMORY; - else - hr = SafeArrayLock(psa); - if (SUCCEEDED(hr)) { - ole_set_safe_array(dim-1, psa, pid, pub, val, dim, (VARTYPE)(vt & VT_TYPEMASK)); - hr = SafeArrayUnlock(psa); - } - - if(pub) free(pub); - if(psab) free(psab); - if(pid) free(pid); - - if (SUCCEEDED(hr)) { - V_VT(var) = vt; - V_ARRAY(var) = psa; - } - else { - if (psa != NULL) - SafeArrayDestroy(psa); - } - return hr; -} - -void -ole_val2variant(VALUE val, VARIANT *var) -{ - struct oledata *pole = NULL; - if(rb_obj_is_kind_of(val, cWIN32OLE)) { - pole = oledata_get_struct(val); - OLE_ADDREF(pole->pDispatch); - V_VT(var) = VT_DISPATCH; - V_DISPATCH(var) = pole->pDispatch; - return; - } - if (rb_obj_is_kind_of(val, cWIN32OLE_VARIANT)) { - ole_variant2variant(val, var); - return; - } - if (rb_obj_is_kind_of(val, cWIN32OLE_RECORD)) { - ole_rec2variant(val, var); - return; - } - if (rb_obj_is_kind_of(val, rb_cTime)) { - V_VT(var) = VT_DATE; - V_DATE(var) = rbtime2vtdate(val); - return; - } - switch (TYPE(val)) { - case T_ARRAY: - ole_val_ary2variant_ary(val, var, VT_VARIANT|VT_ARRAY); - break; - case T_STRING: - V_VT(var) = VT_BSTR; - V_BSTR(var) = ole_vstr2wc(val); - break; - case T_FIXNUM: - V_VT(var) = VT_I4; - { - long v = RB_NUM2LONG(val); - V_I4(var) = (LONG)v; -#if SIZEOF_LONG > 4 - if (V_I4(var) != v) { - V_I8(var) = v; - V_VT(var) = VT_I8; - } -#endif - } - break; - case T_BIGNUM: - V_VT(var) = VT_R8; - V_R8(var) = rb_big2dbl(val); - break; - case T_FLOAT: - V_VT(var) = VT_R8; - V_R8(var) = NUM2DBL(val); - break; - case T_TRUE: - V_VT(var) = VT_BOOL; - V_BOOL(var) = VARIANT_TRUE; - break; - case T_FALSE: - V_VT(var) = VT_BOOL; - V_BOOL(var) = VARIANT_FALSE; - break; - case T_NIL: - if (g_nil_to == VT_ERROR) { - V_VT(var) = VT_ERROR; - V_ERROR(var) = DISP_E_PARAMNOTFOUND; - }else { - V_VT(var) = VT_EMPTY; - } - break; - default: - V_VT(var) = VT_DISPATCH; - V_DISPATCH(var) = val2dispatch(val); - break; - } -} - -void -ole_val2variant2(VALUE val, VARIANT *var) -{ - g_nil_to = VT_EMPTY; - ole_val2variant(val, var); - g_nil_to = VT_ERROR; -} - -VALUE -make_inspect(const char *class_name, VALUE detail) -{ - VALUE str; - str = rb_str_new2("#<"); - rb_str_cat2(str, class_name); - rb_str_cat2(str, ":"); - rb_str_concat(str, detail); - rb_str_cat2(str, ">"); - return str; -} - -VALUE -default_inspect(VALUE self, const char *class_name) -{ - VALUE detail = rb_funcall(self, rb_intern("to_s"), 0); - return make_inspect(class_name, detail); -} - -static VALUE -ole_set_member(VALUE self, IDispatch *dispatch) -{ - struct oledata *pole = NULL; - pole = oledata_get_struct(self); - if (pole->pDispatch) { - OLE_RELEASE(pole->pDispatch); - pole->pDispatch = NULL; - } - pole->pDispatch = dispatch; - return self; -} - - -static VALUE -fole_s_allocate(VALUE klass) -{ - struct oledata *pole; - VALUE obj; - ole_initialize(); - obj = TypedData_Make_Struct(klass, struct oledata, &ole_datatype, pole); - pole->pDispatch = NULL; - return obj; -} - -static VALUE -create_win32ole_object(VALUE klass, IDispatch *pDispatch, int argc, VALUE *argv) -{ - VALUE obj = fole_s_allocate(klass); - ole_set_member(obj, pDispatch); - return obj; -} - -static VALUE -ary_new_dim(VALUE myary, LONG *pid, LONG *plb, LONG dim) { - long i; - VALUE obj = Qnil; - VALUE pobj = Qnil; - long *ids = ALLOC_N(long, dim); - if (!ids) { - rb_raise(rb_eRuntimeError, "memory allocation error"); - } - for(i = 0; i < dim; i++) { - ids[i] = pid[i] - plb[i]; - } - obj = myary; - pobj = myary; - for(i = 0; i < dim-1; i++) { - obj = rb_ary_entry(pobj, ids[i]); - if (obj == Qnil) { - rb_ary_store(pobj, ids[i], rb_ary_new()); - } - obj = rb_ary_entry(pobj, ids[i]); - pobj = obj; - } - if (ids) free(ids); - return obj; -} - -static void -ary_store_dim(VALUE myary, LONG *pid, LONG *plb, LONG dim, VALUE val) { - long id = pid[dim - 1] - plb[dim - 1]; - VALUE obj = ary_new_dim(myary, pid, plb, dim); - rb_ary_store(obj, id, val); -} - -VALUE -ole_variant2val(VARIANT *pvar) -{ - VALUE obj = Qnil; - VARTYPE vt = V_VT(pvar); - HRESULT hr; - while ( vt == (VT_BYREF | VT_VARIANT) ) { - pvar = V_VARIANTREF(pvar); - vt = V_VT(pvar); - } - - if(V_ISARRAY(pvar)) { - VARTYPE vt_base = vt & VT_TYPEMASK; - SAFEARRAY *psa = V_ISBYREF(pvar) ? *V_ARRAYREF(pvar) : V_ARRAY(pvar); - UINT i = 0; - LONG *pid, *plb, *pub; - VARIANT variant; - VALUE val; - UINT dim = 0; - if (!psa) { - return obj; - } - dim = SafeArrayGetDim(psa); - pid = ALLOC_N(LONG, dim); - plb = ALLOC_N(LONG, dim); - pub = ALLOC_N(LONG, dim); - - if(!pid || !plb || !pub) { - if(pid) free(pid); - if(plb) free(plb); - if(pub) free(pub); - rb_raise(rb_eRuntimeError, "memory allocation error"); - } - - for(i = 0; i < dim; ++i) { - SafeArrayGetLBound(psa, i+1, &plb[i]); - SafeArrayGetLBound(psa, i+1, &pid[i]); - SafeArrayGetUBound(psa, i+1, &pub[i]); - } - hr = SafeArrayLock(psa); - if (SUCCEEDED(hr)) { - obj = rb_ary_new(); - i = 0; - VariantInit(&variant); - V_VT(&variant) = vt_base | VT_BYREF; - if (vt_base == VT_RECORD) { - hr = SafeArrayGetRecordInfo(psa, &V_RECORDINFO(&variant)); - if (SUCCEEDED(hr)) { - V_VT(&variant) = VT_RECORD; - } - } - while (i < dim) { - ary_new_dim(obj, pid, plb, dim); - if (vt_base == VT_RECORD) - hr = SafeArrayPtrOfIndex(psa, pid, &V_RECORD(&variant)); - else - hr = SafeArrayPtrOfIndex(psa, pid, &V_BYREF(&variant)); - if (SUCCEEDED(hr)) { - val = ole_variant2val(&variant); - ary_store_dim(obj, pid, plb, dim, val); - } - for (i = 0; i < dim; ++i) { - if (++pid[i] <= pub[i]) - break; - pid[i] = plb[i]; - } - } - SafeArrayUnlock(psa); - } - if(pid) free(pid); - if(plb) free(plb); - if(pub) free(pub); - return obj; - } - switch(V_VT(pvar) & ~VT_BYREF){ - case VT_EMPTY: - break; - case VT_NULL: - break; - case VT_I1: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM((long)*V_I1REF(pvar)); - else - obj = RB_INT2NUM((long)V_I1(pvar)); - break; - - case VT_UI1: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM((long)*V_UI1REF(pvar)); - else - obj = RB_INT2NUM((long)V_UI1(pvar)); - break; - - case VT_I2: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM((long)*V_I2REF(pvar)); - else - obj = RB_INT2NUM((long)V_I2(pvar)); - break; - - case VT_UI2: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM((long)*V_UI2REF(pvar)); - else - obj = RB_INT2NUM((long)V_UI2(pvar)); - break; - - case VT_I4: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM((long)*V_I4REF(pvar)); - else - obj = RB_INT2NUM((long)V_I4(pvar)); - break; - - case VT_UI4: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM((long)*V_UI4REF(pvar)); - else - obj = RB_INT2NUM((long)V_UI4(pvar)); - break; - - case VT_INT: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM((long)*V_INTREF(pvar)); - else - obj = RB_INT2NUM((long)V_INT(pvar)); - break; - - case VT_UINT: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM((long)*V_UINTREF(pvar)); - else - obj = RB_INT2NUM((long)V_UINT(pvar)); - break; - -#if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) - case VT_I8: - if(V_ISBYREF(pvar)) -#if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) -#ifdef V_I8REF - obj = I8_2_NUM(*V_I8REF(pvar)); -#endif -#else - obj = Qnil; -#endif - else - obj = I8_2_NUM(V_I8(pvar)); - break; - case VT_UI8: - if(V_ISBYREF(pvar)) -#if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) -#ifdef V_UI8REF - obj = UI8_2_NUM(*V_UI8REF(pvar)); -#endif -#else - obj = Qnil; -#endif - else - obj = UI8_2_NUM(V_UI8(pvar)); - break; -#endif /* (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) */ - - case VT_R4: - if(V_ISBYREF(pvar)) - obj = rb_float_new(*V_R4REF(pvar)); - else - obj = rb_float_new(V_R4(pvar)); - break; - - case VT_R8: - if(V_ISBYREF(pvar)) - obj = rb_float_new(*V_R8REF(pvar)); - else - obj = rb_float_new(V_R8(pvar)); - break; - - case VT_BSTR: - { - BSTR bstr; - if(V_ISBYREF(pvar)) - bstr = *V_BSTRREF(pvar); - else - bstr = V_BSTR(pvar); - obj = (SysStringLen(bstr) == 0) - ? rb_str_new2("") - : ole_wc2vstr(bstr, FALSE); - break; - } - - case VT_ERROR: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM(*V_ERRORREF(pvar)); - else - obj = RB_INT2NUM(V_ERROR(pvar)); - break; - - case VT_BOOL: - if (V_ISBYREF(pvar)) - obj = (*V_BOOLREF(pvar) ? Qtrue : Qfalse); - else - obj = (V_BOOL(pvar) ? Qtrue : Qfalse); - break; - - case VT_DISPATCH: - { - IDispatch *pDispatch; - - if (V_ISBYREF(pvar)) - pDispatch = *V_DISPATCHREF(pvar); - else - pDispatch = V_DISPATCH(pvar); - - if (pDispatch != NULL ) { - OLE_ADDREF(pDispatch); - obj = create_win32ole_object(cWIN32OLE, pDispatch, 0, 0); - } - break; - } - - case VT_UNKNOWN: - { - /* get IDispatch interface from IUnknown interface */ - IUnknown *punk; - IDispatch *pDispatch; - void *p; - HRESULT hr; - - if (V_ISBYREF(pvar)) - punk = *V_UNKNOWNREF(pvar); - else - punk = V_UNKNOWN(pvar); - - if(punk != NULL) { - hr = punk->lpVtbl->QueryInterface(punk, &IID_IDispatch, &p); - if(SUCCEEDED(hr)) { - pDispatch = p; - obj = create_win32ole_object(cWIN32OLE, pDispatch, 0, 0); - } - } - break; - } - - case VT_DATE: - { - DATE date; - if(V_ISBYREF(pvar)) - date = *V_DATEREF(pvar); - else - date = V_DATE(pvar); - - obj = vtdate2rbtime(date); - break; - } - - case VT_RECORD: - { - IRecordInfo *pri = V_RECORDINFO(pvar); - void *prec = V_RECORD(pvar); - obj = create_win32ole_record(pri, prec); - break; - } - - case VT_CY: - default: - { - HRESULT hr; - VARIANT variant; - VariantInit(&variant); - hr = VariantChangeTypeEx(&variant, pvar, - cWIN32OLE_lcid, 0, VT_BSTR); - if (SUCCEEDED(hr) && V_VT(&variant) == VT_BSTR) { - obj = ole_wc2vstr(V_BSTR(&variant), FALSE); - } - VariantClear(&variant); - break; - } - } - return obj; -} - -LONG -reg_open_key(HKEY hkey, const char *name, HKEY *phkey) -{ - return RegOpenKeyEx(hkey, name, 0, KEY_READ, phkey); -} - -LONG -reg_open_vkey(HKEY hkey, VALUE key, HKEY *phkey) -{ - return reg_open_key(hkey, StringValuePtr(key), phkey); -} - -VALUE -reg_enum_key(HKEY hkey, DWORD i) -{ - char buf[BUFSIZ + 1]; - DWORD size_buf = sizeof(buf); - FILETIME ft; - LONG err = RegEnumKeyEx(hkey, i, buf, &size_buf, - NULL, NULL, NULL, &ft); - if(err == ERROR_SUCCESS) { - buf[BUFSIZ] = '\0'; - return rb_str_new2(buf); - } - return Qnil; -} - -VALUE -reg_get_val(HKEY hkey, const char *subkey) -{ - char *pbuf; - DWORD dwtype = 0; - DWORD size = 0; - VALUE val = Qnil; - LONG err = RegQueryValueEx(hkey, subkey, NULL, &dwtype, NULL, &size); - - if (err == ERROR_SUCCESS) { - pbuf = ALLOC_N(char, size + 1); - err = RegQueryValueEx(hkey, subkey, NULL, &dwtype, (BYTE *)pbuf, &size); - if (err == ERROR_SUCCESS) { - pbuf[size] = '\0'; - if (dwtype == REG_EXPAND_SZ) { - char* pbuf2 = (char *)pbuf; - DWORD len = ExpandEnvironmentStrings(pbuf2, NULL, 0); - pbuf = ALLOC_N(char, len + 1); - ExpandEnvironmentStrings(pbuf2, pbuf, len + 1); - free(pbuf2); - } - val = rb_str_new2((char *)pbuf); - } - free(pbuf); - } - return val; -} - -VALUE -reg_get_val2(HKEY hkey, const char *subkey) -{ - HKEY hsubkey; - LONG err; - VALUE val = Qnil; - err = RegOpenKeyEx(hkey, subkey, 0, KEY_READ, &hsubkey); - if (err == ERROR_SUCCESS) { - val = reg_get_val(hsubkey, NULL); - RegCloseKey(hsubkey); - } - if (val == Qnil) { - val = reg_get_val(hkey, subkey); - } - return val; -} - -static void -ole_const_load(ITypeLib *pTypeLib, VALUE klass, VALUE self) -{ - unsigned int count; - unsigned int index; - int iVar; - ITypeInfo *pTypeInfo; - TYPEATTR *pTypeAttr; - VARDESC *pVarDesc; - HRESULT hr; - unsigned int len; - BSTR bstr; - char *pName = NULL; - VALUE val; - VALUE constant; - ID id; - constant = rb_hash_new(); - count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib); - for (index = 0; index < count; index++) { - hr = pTypeLib->lpVtbl->GetTypeInfo(pTypeLib, index, &pTypeInfo); - if (FAILED(hr)) - continue; - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if(FAILED(hr)) { - OLE_RELEASE(pTypeInfo); - continue; - } - for(iVar = 0; iVar < pTypeAttr->cVars; iVar++) { - hr = pTypeInfo->lpVtbl->GetVarDesc(pTypeInfo, iVar, &pVarDesc); - if(FAILED(hr)) - continue; - if(pVarDesc->varkind == VAR_CONST && - !(pVarDesc->wVarFlags & (VARFLAG_FHIDDEN | - VARFLAG_FRESTRICTED | - VARFLAG_FNONBROWSABLE))) { - hr = pTypeInfo->lpVtbl->GetNames(pTypeInfo, pVarDesc->memid, &bstr, - 1, &len); - if(FAILED(hr) || len == 0 || !bstr) - continue; - pName = ole_wc2mb(bstr); - val = ole_variant2val(V_UNION1(pVarDesc, lpvarValue)); - *pName = toupper((int)*pName); - id = rb_intern(pName); - if (rb_is_const_id(id)) { - if(!rb_const_defined_at(klass, id)) { - rb_define_const(klass, pName, val); - } - } - else { - rb_hash_aset(constant, rb_str_new2(pName), val); - } - SysFreeString(bstr); - if(pName) { - free(pName); - pName = NULL; - } - } - pTypeInfo->lpVtbl->ReleaseVarDesc(pTypeInfo, pVarDesc); - } - pTypeInfo->lpVtbl->ReleaseTypeAttr(pTypeInfo, pTypeAttr); - OLE_RELEASE(pTypeInfo); - } - rb_define_const(klass, "CONSTANTS", constant); -} - -static HRESULT -clsid_from_remote(VALUE host, VALUE com, CLSID *pclsid) -{ - HKEY hlm; - HKEY hpid; - VALUE subkey; - LONG err; - char clsid[100]; - OLECHAR *pbuf; - DWORD len; - DWORD dwtype; - HRESULT hr = S_OK; - err = RegConnectRegistry(StringValuePtr(host), HKEY_LOCAL_MACHINE, &hlm); - if (err != ERROR_SUCCESS) - return HRESULT_FROM_WIN32(err); - subkey = rb_str_new2("SOFTWARE\\Classes\\"); - rb_str_concat(subkey, com); - rb_str_cat2(subkey, "\\CLSID"); - err = RegOpenKeyEx(hlm, StringValuePtr(subkey), 0, KEY_READ, &hpid); - if (err != ERROR_SUCCESS) - hr = HRESULT_FROM_WIN32(err); - else { - len = sizeof(clsid); - err = RegQueryValueEx(hpid, "", NULL, &dwtype, (BYTE *)clsid, &len); - if (err == ERROR_SUCCESS && dwtype == REG_SZ) { - pbuf = ole_mb2wc(clsid, -1, cWIN32OLE_cp); - hr = CLSIDFromString(pbuf, pclsid); - SysFreeString(pbuf); - } - else { - hr = HRESULT_FROM_WIN32(err); - } - RegCloseKey(hpid); - } - RegCloseKey(hlm); - return hr; -} - -static VALUE -ole_create_dcom(VALUE self, VALUE ole, VALUE host, VALUE others) -{ - HRESULT hr; - CLSID clsid; - OLECHAR *pbuf; - - COSERVERINFO serverinfo; - MULTI_QI multi_qi; - DWORD clsctx = CLSCTX_REMOTE_SERVER; - - if (!gole32) - gole32 = LoadLibrary("OLE32"); - if (!gole32) - rb_raise(rb_eRuntimeError, "failed to load OLE32"); - if (!gCoCreateInstanceEx) - gCoCreateInstanceEx = (FNCOCREATEINSTANCEEX*) - GetProcAddress(gole32, "CoCreateInstanceEx"); - if (!gCoCreateInstanceEx) - rb_raise(rb_eRuntimeError, "CoCreateInstanceEx is not supported in this environment"); - - pbuf = ole_vstr2wc(ole); - hr = CLSIDFromProgID(pbuf, &clsid); - if (FAILED(hr)) - hr = clsid_from_remote(host, ole, &clsid); - if (FAILED(hr)) - hr = CLSIDFromString(pbuf, &clsid); - SysFreeString(pbuf); - if (FAILED(hr)) - ole_raise(hr, eWIN32OLERuntimeError, - "unknown OLE server: `%s'", - StringValuePtr(ole)); - memset(&serverinfo, 0, sizeof(COSERVERINFO)); - serverinfo.pwszName = ole_vstr2wc(host); - memset(&multi_qi, 0, sizeof(MULTI_QI)); - multi_qi.pIID = &IID_IDispatch; - hr = gCoCreateInstanceEx(&clsid, NULL, clsctx, &serverinfo, 1, &multi_qi); - SysFreeString(serverinfo.pwszName); - if (FAILED(hr)) - ole_raise(hr, eWIN32OLERuntimeError, - "failed to create DCOM server `%s' in `%s'", - StringValuePtr(ole), - StringValuePtr(host)); - - ole_set_member(self, (IDispatch*)multi_qi.pItf); - return self; -} - -static VALUE -ole_bind_obj(VALUE moniker, int argc, VALUE *argv, VALUE self) -{ - IBindCtx *pBindCtx; - IMoniker *pMoniker; - IDispatch *pDispatch; - void *p; - HRESULT hr; - OLECHAR *pbuf; - ULONG eaten = 0; - - ole_initialize(); - - hr = CreateBindCtx(0, &pBindCtx); - if(FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, - "failed to create bind context"); - } - - pbuf = ole_vstr2wc(moniker); - hr = MkParseDisplayName(pBindCtx, pbuf, &eaten, &pMoniker); - SysFreeString(pbuf); - if(FAILED(hr)) { - OLE_RELEASE(pBindCtx); - ole_raise(hr, eWIN32OLERuntimeError, - "failed to parse display name of moniker `%s'", - StringValuePtr(moniker)); - } - hr = pMoniker->lpVtbl->BindToObject(pMoniker, pBindCtx, NULL, - &IID_IDispatch, &p); - pDispatch = p; - OLE_RELEASE(pMoniker); - OLE_RELEASE(pBindCtx); - - if(FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, - "failed to bind moniker `%s'", - StringValuePtr(moniker)); - } - return create_win32ole_object(self, pDispatch, argc, argv); -} - -/* - * call-seq: - * WIN32OLE.connect( ole ) --> aWIN32OLE - * - * Returns running OLE Automation object or WIN32OLE object from moniker. - * 1st argument should be OLE program id or class id or moniker. - * - * WIN32OLE.connect('Excel.Application') # => WIN32OLE object which represents running Excel. - */ -static VALUE -fole_s_connect(int argc, VALUE *argv, VALUE self) -{ - VALUE svr_name; - VALUE others; - HRESULT hr; - CLSID clsid; - OLECHAR *pBuf; - IDispatch *pDispatch; - void *p; - IUnknown *pUnknown; - - /* initialize to use OLE */ - ole_initialize(); - - rb_scan_args(argc, argv, "1*", &svr_name, &others); - StringValue(svr_name); - - /* get CLSID from OLE server name */ - pBuf = ole_vstr2wc(svr_name); - hr = CLSIDFromProgID(pBuf, &clsid); - if(FAILED(hr)) { - hr = CLSIDFromString(pBuf, &clsid); - } - SysFreeString(pBuf); - if(FAILED(hr)) { - return ole_bind_obj(svr_name, argc, argv, self); - } - - hr = GetActiveObject(&clsid, 0, &pUnknown); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, - "OLE server `%s' not running", StringValuePtr(svr_name)); - } - hr = pUnknown->lpVtbl->QueryInterface(pUnknown, &IID_IDispatch, &p); - pDispatch = p; - if(FAILED(hr)) { - OLE_RELEASE(pUnknown); - ole_raise(hr, eWIN32OLERuntimeError, - "failed to create WIN32OLE server `%s'", - StringValuePtr(svr_name)); - } - - OLE_RELEASE(pUnknown); - - return create_win32ole_object(self, pDispatch, argc, argv); -} - -/* - * call-seq: - * WIN32OLE.const_load( ole, mod = WIN32OLE) - * - * Defines the constants of OLE Automation server as mod's constants. - * The first argument is WIN32OLE object or type library name. - * If 2nd argument is omitted, the default is WIN32OLE. - * The first letter of Ruby's constant variable name is upper case, - * so constant variable name of WIN32OLE object is capitalized. - * For example, the 'xlTop' constant of Excel is changed to 'XlTop' - * in WIN32OLE. - * If the first letter of constant variable is not [A-Z], then - * the constant is defined as CONSTANTS hash element. - * - * module EXCEL_CONST - * end - * excel = WIN32OLE.new('Excel.Application') - * WIN32OLE.const_load(excel, EXCEL_CONST) - * puts EXCEL_CONST::XlTop # => -4160 - * puts EXCEL_CONST::CONSTANTS['_xlDialogChartSourceData'] # => 541 - * - * WIN32OLE.const_load(excel) - * puts WIN32OLE::XlTop # => -4160 - * - * module MSO - * end - * WIN32OLE.const_load('Microsoft Office 9.0 Object Library', MSO) - * puts MSO::MsoLineSingle # => 1 - */ -static VALUE -fole_s_const_load(int argc, VALUE *argv, VALUE self) -{ - VALUE ole; - VALUE klass; - struct oledata *pole = NULL; - ITypeInfo *pTypeInfo; - ITypeLib *pTypeLib; - unsigned int index; - HRESULT hr; - OLECHAR *pBuf; - VALUE file; - LCID lcid = cWIN32OLE_lcid; - - rb_scan_args(argc, argv, "11", &ole, &klass); - if (!RB_TYPE_P(klass, T_CLASS) && - !RB_TYPE_P(klass, T_MODULE) && - !RB_TYPE_P(klass, T_NIL)) { - rb_raise(rb_eTypeError, "2nd parameter must be Class or Module"); - } - if (rb_obj_is_kind_of(ole, cWIN32OLE)) { - pole = oledata_get_struct(ole); - hr = pole->pDispatch->lpVtbl->GetTypeInfo(pole->pDispatch, - 0, lcid, &pTypeInfo); - if(FAILED(hr)) { - ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeInfo"); - } - hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, &pTypeLib, &index); - if(FAILED(hr)) { - OLE_RELEASE(pTypeInfo); - ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetContainingTypeLib"); - } - OLE_RELEASE(pTypeInfo); - if(!RB_TYPE_P(klass, T_NIL)) { - ole_const_load(pTypeLib, klass, self); - } - else { - ole_const_load(pTypeLib, cWIN32OLE, self); - } - OLE_RELEASE(pTypeLib); - } - else if(RB_TYPE_P(ole, T_STRING)) { - file = typelib_file(ole); - if (file == Qnil) { - file = ole; - } - pBuf = ole_vstr2wc(file); - hr = LoadTypeLibEx(pBuf, REGKIND_NONE, &pTypeLib); - SysFreeString(pBuf); - if (FAILED(hr)) - ole_raise(hr, eWIN32OLERuntimeError, "failed to LoadTypeLibEx"); - if(!RB_TYPE_P(klass, T_NIL)) { - ole_const_load(pTypeLib, klass, self); - } - else { - ole_const_load(pTypeLib, cWIN32OLE, self); - } - OLE_RELEASE(pTypeLib); - } - else { - rb_raise(rb_eTypeError, "1st parameter must be WIN32OLE instance"); - } - return Qnil; -} - -static ULONG -reference_count(struct oledata * pole) -{ - ULONG n = 0; - if(pole->pDispatch) { - OLE_ADDREF(pole->pDispatch); - n = OLE_RELEASE(pole->pDispatch); - } - return n; -} - -/* - * call-seq: - * WIN32OLE.ole_reference_count(aWIN32OLE) --> number - * - * Returns reference counter of Dispatch interface of WIN32OLE object. - * You should not use this method because this method - * exists only for debugging WIN32OLE. - */ -static VALUE -fole_s_reference_count(VALUE self, VALUE obj) -{ - struct oledata * pole = NULL; - pole = oledata_get_struct(obj); - return RB_INT2NUM(reference_count(pole)); -} - -/* - * call-seq: - * WIN32OLE.ole_free(aWIN32OLE) --> number - * - * Invokes Release method of Dispatch interface of WIN32OLE object. - * You should not use this method because this method - * exists only for debugging WIN32OLE. - * The return value is reference counter of OLE object. - */ -static VALUE -fole_s_free(VALUE self, VALUE obj) -{ - ULONG n = 0; - struct oledata * pole = NULL; - pole = oledata_get_struct(obj); - if(pole->pDispatch) { - if (reference_count(pole) > 0) { - n = OLE_RELEASE(pole->pDispatch); - } - } - return RB_INT2NUM(n); -} - -static HWND -ole_show_help(VALUE helpfile, VALUE helpcontext) -{ - FNHTMLHELP *pfnHtmlHelp; - HWND hwnd = 0; - - if(!ghhctrl) - ghhctrl = LoadLibrary("HHCTRL.OCX"); - if (!ghhctrl) - return hwnd; - pfnHtmlHelp = (FNHTMLHELP*)GetProcAddress(ghhctrl, "HtmlHelpA"); - if (!pfnHtmlHelp) - return hwnd; - hwnd = pfnHtmlHelp(GetDesktopWindow(), StringValuePtr(helpfile), - 0x0f, RB_NUM2INT(helpcontext)); - if (hwnd == 0) - hwnd = pfnHtmlHelp(GetDesktopWindow(), StringValuePtr(helpfile), - 0, RB_NUM2INT(helpcontext)); - return hwnd; -} - -/* - * call-seq: - * WIN32OLE.ole_show_help(obj [,helpcontext]) - * - * Displays helpfile. The 1st argument specifies WIN32OLE_TYPE - * object or WIN32OLE_METHOD object or helpfile. - * - * excel = WIN32OLE.new('Excel.Application') - * typeobj = excel.ole_type - * WIN32OLE.ole_show_help(typeobj) - */ -static VALUE -fole_s_show_help(int argc, VALUE *argv, VALUE self) -{ - VALUE target; - VALUE helpcontext; - VALUE helpfile; - VALUE name; - HWND hwnd; - rb_scan_args(argc, argv, "11", &target, &helpcontext); - if (rb_obj_is_kind_of(target, cWIN32OLE_TYPE) || - rb_obj_is_kind_of(target, cWIN32OLE_METHOD)) { - helpfile = rb_funcall(target, rb_intern("helpfile"), 0); - if(strlen(StringValuePtr(helpfile)) == 0) { - name = rb_ivar_get(target, rb_intern("name")); - rb_raise(rb_eRuntimeError, "no helpfile of `%s'", - StringValuePtr(name)); - } - helpcontext = rb_funcall(target, rb_intern("helpcontext"), 0); - } else { - helpfile = target; - } - if (!RB_TYPE_P(helpfile, T_STRING)) { - rb_raise(rb_eTypeError, "1st parameter must be (String|WIN32OLE_TYPE|WIN32OLE_METHOD)"); - } - hwnd = ole_show_help(helpfile, helpcontext); - if(hwnd == 0) { - rb_raise(rb_eRuntimeError, "failed to open help file `%s'", - StringValuePtr(helpfile)); - } - return Qnil; -} - -/* - * call-seq: - * WIN32OLE.codepage - * - * Returns current codepage. - * WIN32OLE.codepage # => WIN32OLE::CP_ACP - */ -static VALUE -fole_s_get_code_page(VALUE self) -{ - return RB_INT2FIX(cWIN32OLE_cp); -} - -static BOOL CALLBACK -installed_code_page_proc(LPTSTR str) { - if (strtoul(str, NULL, 10) == g_cp_to_check) { - g_cp_installed = TRUE; - return FALSE; - } - return TRUE; -} - -static BOOL -code_page_installed(UINT cp) -{ - g_cp_installed = FALSE; - g_cp_to_check = cp; - EnumSystemCodePages(installed_code_page_proc, CP_INSTALLED); - return g_cp_installed; -} - -/* - * call-seq: - * WIN32OLE.codepage = CP - * - * Sets current codepage. - * The WIN32OLE.codepage is initialized according to - * Encoding.default_internal. - * If Encoding.default_internal is nil then WIN32OLE.codepage - * is initialized according to Encoding.default_external. - * - * WIN32OLE.codepage = WIN32OLE::CP_UTF8 - * WIN32OLE.codepage = 65001 - */ -static VALUE -fole_s_set_code_page(VALUE self, VALUE vcp) -{ - UINT cp = RB_FIX2INT(vcp); - set_ole_codepage(cp); - /* - * Should this method return old codepage? - */ - return Qnil; -} - -/* - * call-seq: - * WIN32OLE.locale -> locale id. - * - * Returns current locale id (lcid). The default locale is - * WIN32OLE::LOCALE_SYSTEM_DEFAULT. - * - * lcid = WIN32OLE.locale - */ -static VALUE -fole_s_get_locale(VALUE self) -{ - return RB_INT2FIX(cWIN32OLE_lcid); -} - -static BOOL -CALLBACK installed_lcid_proc(LPTSTR str) -{ - if (strcmp(str, g_lcid_to_check) == 0) { - g_lcid_installed = TRUE; - return FALSE; - } - return TRUE; -} - -static BOOL -lcid_installed(LCID lcid) -{ - g_lcid_installed = FALSE; - snprintf(g_lcid_to_check, sizeof(g_lcid_to_check), "%08lx", (unsigned long)lcid); - EnumSystemLocales(installed_lcid_proc, LCID_INSTALLED); - return g_lcid_installed; -} - -/* - * call-seq: - * WIN32OLE.locale = lcid - * - * Sets current locale id (lcid). - * - * WIN32OLE.locale = 1033 # set locale English(U.S) - * obj = WIN32OLE_VARIANT.new("$100,000", WIN32OLE::VARIANT::VT_CY) - * - */ -static VALUE -fole_s_set_locale(VALUE self, VALUE vlcid) -{ - LCID lcid = RB_FIX2INT(vlcid); - if (lcid_installed(lcid)) { - cWIN32OLE_lcid = lcid; - } else { - switch (lcid) { - case LOCALE_SYSTEM_DEFAULT: - case LOCALE_USER_DEFAULT: - cWIN32OLE_lcid = lcid; - break; - default: - rb_raise(eWIN32OLERuntimeError, "not installed locale: %u", (unsigned int)lcid); - } - } - return Qnil; -} - -/* - * call-seq: - * WIN32OLE.create_guid - * - * Creates GUID. - * WIN32OLE.create_guid # => {1CB530F1-F6B1-404D-BCE6-1959BF91F4A8} - */ -static VALUE -fole_s_create_guid(VALUE self) -{ - GUID guid; - HRESULT hr; - OLECHAR bstr[80]; - int len = 0; - hr = CoCreateGuid(&guid); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, "failed to create GUID"); - } - len = StringFromGUID2(&guid, bstr, sizeof(bstr)/sizeof(OLECHAR)); - if (len == 0) { - rb_raise(rb_eRuntimeError, "failed to create GUID(buffer over)"); - } - return ole_wc2vstr(bstr, FALSE); -} - -/* - * WIN32OLE.ole_initialize and WIN32OLE.ole_uninitialize - * are used in win32ole.rb to fix the issue bug #2618 (ruby-core:27634). - * You must not use these method. - */ - -/* :nodoc: */ -static VALUE -fole_s_ole_initialize(VALUE self) -{ - ole_initialize(); - return Qnil; -} - -/* :nodoc: */ -static VALUE -fole_s_ole_uninitialize(VALUE self) -{ - ole_uninitialize(); - return Qnil; -} - -/* - * Document-class: WIN32OLE - * - * <code>WIN32OLE</code> objects represent OLE Automation object in Ruby. - * - * By using WIN32OLE, you can access OLE server like VBScript. - * - * Here is sample script. - * - * require 'win32ole' - * - * excel = WIN32OLE.new('Excel.Application') - * excel.visible = true - * workbook = excel.Workbooks.Add(); - * worksheet = workbook.Worksheets(1); - * worksheet.Range("A1:D1").value = ["North","South","East","West"]; - * worksheet.Range("A2:B2").value = [5.2, 10]; - * worksheet.Range("C2").value = 8; - * worksheet.Range("D2").value = 20; - * - * range = worksheet.Range("A1:D2"); - * range.select - * chart = workbook.Charts.Add; - * - * workbook.saved = true; - * - * excel.ActiveWorkbook.Close(0); - * excel.Quit(); - * - * Unfortunately, Win32OLE doesn't support the argument passed by - * reference directly. - * Instead, Win32OLE provides WIN32OLE::ARGV or WIN32OLE_VARIANT object. - * If you want to get the result value of argument passed by reference, - * you can use WIN32OLE::ARGV or WIN32OLE_VARIANT. - * - * oleobj.method(arg1, arg2, refargv3) - * puts WIN32OLE::ARGV[2] # the value of refargv3 after called oleobj.method - * - * or - * - * refargv3 = WIN32OLE_VARIANT.new(XXX, - * WIN32OLE::VARIANT::VT_BYREF|WIN32OLE::VARIANT::VT_XXX) - * oleobj.method(arg1, arg2, refargv3) - * p refargv3.value # the value of refargv3 after called oleobj.method. - * - */ - -/* - * call-seq: - * WIN32OLE.new(server, [host]) -> WIN32OLE object - * WIN32OLE.new(server, license: 'key') -> WIN32OLE object - * - * Returns a new WIN32OLE object(OLE Automation object). - * The first argument server specifies OLE Automation server. - * The first argument should be CLSID or PROGID. - * If second argument host specified, then returns OLE Automation - * object on host. - * If :license keyword argument is provided, - * IClassFactory2::CreateInstanceLic is used to create instance of - * licensed server. - * - * WIN32OLE.new('Excel.Application') # => Excel OLE Automation WIN32OLE object. - * WIN32OLE.new('{00024500-0000-0000-C000-000000000046}') # => Excel OLE Automation WIN32OLE object. - */ -static VALUE -fole_initialize(int argc, VALUE *argv, VALUE self) -{ - VALUE svr_name; - VALUE host; - VALUE others; - VALUE opts; - HRESULT hr; - CLSID clsid; - OLECHAR *pBuf; - OLECHAR *key_buf; - IDispatch *pDispatch; - IClassFactory2 * pIClassFactory2; - void *p; - static ID keyword_ids[1]; - VALUE kwargs[1]; - - rb_call_super(0, 0); - rb_scan_args(argc, argv, "11*:", &svr_name, &host, &others, &opts); - - StringValue(svr_name); - if (!NIL_P(host)) { - StringValue(host); - return ole_create_dcom(self, svr_name, host, others); - } - - /* get CLSID from OLE server name */ - pBuf = ole_vstr2wc(svr_name); - hr = CLSIDFromProgID(pBuf, &clsid); - if(FAILED(hr)) { - hr = CLSIDFromString(pBuf, &clsid); - } - SysFreeString(pBuf); - if(FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, - "unknown OLE server: `%s'", - StringValuePtr(svr_name)); - } - - if (!keyword_ids[0]) { - keyword_ids[0] = rb_intern_const("license"); - } - rb_get_kwargs(opts, keyword_ids, 0, 1, kwargs); - - if (kwargs[0] == Qundef) { - /* get IDispatch interface */ - hr = CoCreateInstance( - &clsid, - NULL, - CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, - &IID_IDispatch, - &p - ); - } else { - hr = CoGetClassObject( - &clsid, - CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, - NULL, - &IID_IClassFactory2, - (LPVOID)&pIClassFactory2 - ); - if (hr == S_OK) { - key_buf = ole_vstr2wc(kwargs[0]); - hr = pIClassFactory2->lpVtbl->CreateInstanceLic(pIClassFactory2, NULL, NULL, &IID_IDispatch, key_buf, &p); - SysFreeString(key_buf); - OLE_RELEASE(pIClassFactory2); - } - } - if(FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, - "failed to create WIN32OLE object from `%s'", - StringValuePtr(svr_name)); - } - pDispatch = p; - - ole_set_member(self, pDispatch); - return self; -} - -static int -hash2named_arg(VALUE key, VALUE val, VALUE pop) -{ - struct oleparam* pOp = (struct oleparam *)pop; - unsigned int index, i; - index = pOp->dp.cNamedArgs; - /*--------------------------------------------- - the data-type of key must be String or Symbol - -----------------------------------------------*/ - if(!RB_TYPE_P(key, T_STRING) && !RB_TYPE_P(key, T_SYMBOL)) { - /* clear name of dispatch parameters */ - for(i = 1; i < index + 1; i++) { - SysFreeString(pOp->pNamedArgs[i]); - } - /* clear dispatch parameters */ - for(i = 0; i < index; i++ ) { - VariantClear(&(pOp->dp.rgvarg[i])); - } - /* raise an exception */ - rb_raise(rb_eTypeError, "wrong argument type (expected String or Symbol)"); - } - if (RB_TYPE_P(key, T_SYMBOL)) { - key = rb_sym2str(key); - } - - /* pNamedArgs[0] is <method name>, so "index + 1" */ - pOp->pNamedArgs[index + 1] = ole_vstr2wc(key); - - VariantInit(&(pOp->dp.rgvarg[index])); - ole_val2variant(val, &(pOp->dp.rgvarg[index])); - - pOp->dp.cNamedArgs += 1; - return ST_CONTINUE; -} - -static VALUE -set_argv(VARIANTARG* realargs, unsigned int beg, unsigned int end) -{ - VALUE argv = rb_const_get(cWIN32OLE, rb_intern("ARGV")); - - Check_Type(argv, T_ARRAY); - rb_ary_clear(argv); - while (end-- > beg) { - rb_ary_push(argv, ole_variant2val(&realargs[end])); - if (V_VT(&realargs[end]) != VT_RECORD) { - VariantClear(&realargs[end]); - } - } - return argv; -} - -static VALUE -ole_invoke(int argc, VALUE *argv, VALUE self, USHORT wFlags, BOOL is_bracket) -{ - LCID lcid = cWIN32OLE_lcid; - struct oledata *pole = NULL; - HRESULT hr; - VALUE cmd; - VALUE paramS; - VALUE param; - VALUE obj; - VALUE v; - - BSTR wcmdname; - - DISPID DispID; - DISPID* pDispID; - EXCEPINFO excepinfo; - VARIANT result; - VARIANTARG* realargs = NULL; - unsigned int argErr = 0; - unsigned int i; - unsigned int cNamedArgs; - int n; - struct oleparam op; - memset(&excepinfo, 0, sizeof(EXCEPINFO)); - - VariantInit(&result); - - op.dp.rgvarg = NULL; - op.dp.rgdispidNamedArgs = NULL; - op.dp.cNamedArgs = 0; - op.dp.cArgs = 0; - - rb_scan_args(argc, argv, "1*", &cmd, ¶mS); - if(!RB_TYPE_P(cmd, T_STRING) && !RB_TYPE_P(cmd, T_SYMBOL) && !is_bracket) { - rb_raise(rb_eTypeError, "method is wrong type (expected String or Symbol)"); - } - if (RB_TYPE_P(cmd, T_SYMBOL)) { - cmd = rb_sym2str(cmd); - } - pole = oledata_get_struct(self); - if(!pole->pDispatch) { - rb_raise(rb_eRuntimeError, "failed to get dispatch interface"); - } - if (is_bracket) { - DispID = DISPID_VALUE; - argc += 1; - rb_ary_unshift(paramS, cmd); - } else { - wcmdname = ole_vstr2wc(cmd); - hr = pole->pDispatch->lpVtbl->GetIDsOfNames( pole->pDispatch, &IID_NULL, - &wcmdname, 1, lcid, &DispID); - SysFreeString(wcmdname); - if(FAILED(hr)) { - return rb_eNoMethodError; - } - } - - /* pick up last argument of method */ - param = rb_ary_entry(paramS, argc-2); - - op.dp.cNamedArgs = 0; - - /* if last arg is hash object */ - if(RB_TYPE_P(param, T_HASH)) { - /*------------------------------------------ - hash object ==> named dispatch parameters - --------------------------------------------*/ - cNamedArgs = rb_long2int((long)RHASH_SIZE(param)); - op.dp.cArgs = cNamedArgs + argc - 2; - op.pNamedArgs = ALLOCA_N(OLECHAR*, cNamedArgs + 1); - op.dp.rgvarg = ALLOCA_N(VARIANTARG, op.dp.cArgs); - - rb_hash_foreach(param, hash2named_arg, (VALUE)&op); - - pDispID = ALLOCA_N(DISPID, cNamedArgs + 1); - op.pNamedArgs[0] = ole_vstr2wc(cmd); - hr = pole->pDispatch->lpVtbl->GetIDsOfNames(pole->pDispatch, - &IID_NULL, - op.pNamedArgs, - op.dp.cNamedArgs + 1, - lcid, pDispID); - for(i = 0; i < op.dp.cNamedArgs + 1; i++) { - SysFreeString(op.pNamedArgs[i]); - op.pNamedArgs[i] = NULL; - } - if(FAILED(hr)) { - /* clear dispatch parameters */ - for(i = 0; i < op.dp.cArgs; i++ ) { - VariantClear(&op.dp.rgvarg[i]); - } - ole_raise(hr, eWIN32OLERuntimeError, - "failed to get named argument info: `%s'", - StringValuePtr(cmd)); - } - op.dp.rgdispidNamedArgs = &(pDispID[1]); - } - else { - cNamedArgs = 0; - op.dp.cArgs = argc - 1; - op.pNamedArgs = ALLOCA_N(OLECHAR*, cNamedArgs + 1); - if (op.dp.cArgs > 0) { - op.dp.rgvarg = ALLOCA_N(VARIANTARG, op.dp.cArgs); - } - } - /*-------------------------------------- - non hash args ==> dispatch parameters - ----------------------------------------*/ - if(op.dp.cArgs > cNamedArgs) { - realargs = ALLOCA_N(VARIANTARG, op.dp.cArgs-cNamedArgs+1); - for(i = cNamedArgs; i < op.dp.cArgs; i++) { - n = op.dp.cArgs - i + cNamedArgs - 1; - VariantInit(&realargs[n]); - VariantInit(&op.dp.rgvarg[n]); - param = rb_ary_entry(paramS, i-cNamedArgs); - if (rb_obj_is_kind_of(param, cWIN32OLE_VARIANT)) { - ole_variant2variant(param, &op.dp.rgvarg[n]); - } else if (rb_obj_is_kind_of(param, cWIN32OLE_RECORD)) { - ole_val2variant(param, &realargs[n]); - op.dp.rgvarg[n] = realargs[n]; - V_VT(&op.dp.rgvarg[n]) = VT_RECORD | VT_BYREF; - } else { - ole_val2variant(param, &realargs[n]); - V_VT(&op.dp.rgvarg[n]) = VT_VARIANT | VT_BYREF; - V_VARIANTREF(&op.dp.rgvarg[n]) = &realargs[n]; - } - } - } - /* apparent you need to call propput, you need this */ - if (wFlags & DISPATCH_PROPERTYPUT) { - if (op.dp.cArgs == 0) - ole_raise(ResultFromScode(E_INVALIDARG), eWIN32OLERuntimeError, "argument error"); - - op.dp.cNamedArgs = 1; - op.dp.rgdispidNamedArgs = ALLOCA_N( DISPID, 1 ); - op.dp.rgdispidNamedArgs[0] = DISPID_PROPERTYPUT; - } - hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, DispID, - &IID_NULL, lcid, wFlags, &op.dp, - &result, &excepinfo, &argErr); - - if (FAILED(hr)) { - /* retry to call args by value */ - if(op.dp.cArgs >= cNamedArgs) { - for(i = cNamedArgs; i < op.dp.cArgs; i++) { - n = op.dp.cArgs - i + cNamedArgs - 1; - param = rb_ary_entry(paramS, i-cNamedArgs); - ole_val2variant(param, &op.dp.rgvarg[n]); - } - if (hr == DISP_E_EXCEPTION) { - ole_freeexceptinfo(&excepinfo); - } - memset(&excepinfo, 0, sizeof(EXCEPINFO)); - VariantInit(&result); - hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, DispID, - &IID_NULL, lcid, wFlags, - &op.dp, &result, - &excepinfo, &argErr); - - /* mega kludge. if a method in WORD is called and we ask - * for a result when one is not returned then - * hResult == DISP_E_EXCEPTION. this only happens on - * functions whose DISPID > 0x8000 */ - if ((hr == DISP_E_EXCEPTION || hr == DISP_E_MEMBERNOTFOUND) && DispID > 0x8000) { - if (hr == DISP_E_EXCEPTION) { - ole_freeexceptinfo(&excepinfo); - } - memset(&excepinfo, 0, sizeof(EXCEPINFO)); - hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, DispID, - &IID_NULL, lcid, wFlags, - &op.dp, NULL, - &excepinfo, &argErr); - - } - for(i = cNamedArgs; i < op.dp.cArgs; i++) { - n = op.dp.cArgs - i + cNamedArgs - 1; - if (V_VT(&op.dp.rgvarg[n]) != VT_RECORD) { - VariantClear(&op.dp.rgvarg[n]); - } - } - } - - if (FAILED(hr)) { - /* retry after converting nil to VT_EMPTY */ - if (op.dp.cArgs > cNamedArgs) { - for(i = cNamedArgs; i < op.dp.cArgs; i++) { - n = op.dp.cArgs - i + cNamedArgs - 1; - param = rb_ary_entry(paramS, i-cNamedArgs); - ole_val2variant2(param, &op.dp.rgvarg[n]); - } - if (hr == DISP_E_EXCEPTION) { - ole_freeexceptinfo(&excepinfo); - } - memset(&excepinfo, 0, sizeof(EXCEPINFO)); - VariantInit(&result); - hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, DispID, - &IID_NULL, lcid, wFlags, - &op.dp, &result, - &excepinfo, &argErr); - for(i = cNamedArgs; i < op.dp.cArgs; i++) { - n = op.dp.cArgs - i + cNamedArgs - 1; - if (V_VT(&op.dp.rgvarg[n]) != VT_RECORD) { - VariantClear(&op.dp.rgvarg[n]); - } - } - } - } - - } - /* clear dispatch parameter */ - if(op.dp.cArgs > cNamedArgs) { - for(i = cNamedArgs; i < op.dp.cArgs; i++) { - n = op.dp.cArgs - i + cNamedArgs - 1; - param = rb_ary_entry(paramS, i-cNamedArgs); - if (rb_obj_is_kind_of(param, cWIN32OLE_VARIANT)) { - ole_val2variant(param, &realargs[n]); - } else if ( rb_obj_is_kind_of(param, cWIN32OLE_RECORD) && - V_VT(&realargs[n]) == VT_RECORD ) { - olerecord_set_ivar(param, V_RECORDINFO(&realargs[n]), V_RECORD(&realargs[n])); - } - } - set_argv(realargs, cNamedArgs, op.dp.cArgs); - } - else { - for(i = 0; i < op.dp.cArgs; i++) { - VariantClear(&op.dp.rgvarg[i]); - } - } - - if (FAILED(hr)) { - v = ole_excepinfo2msg(&excepinfo); - ole_raise(hr, eWIN32OLERuntimeError, "(in OLE method `%s': )%s", - StringValuePtr(cmd), - StringValuePtr(v)); - } - obj = ole_variant2val(&result); - VariantClear(&result); - return obj; -} - -/* - * call-seq: - * WIN32OLE#invoke(method, [arg1,...]) => return value of method. - * - * Runs OLE method. - * The first argument specifies the method name of OLE Automation object. - * The others specify argument of the <i>method</i>. - * If you can not execute <i>method</i> directly, then use this method instead. - * - * excel = WIN32OLE.new('Excel.Application') - * excel.invoke('Quit') # => same as excel.Quit - * - */ -static VALUE -fole_invoke(int argc, VALUE *argv, VALUE self) -{ - VALUE v = ole_invoke(argc, argv, self, DISPATCH_METHOD|DISPATCH_PROPERTYGET, FALSE); - if (v == rb_eNoMethodError) { - return rb_call_super(argc, argv); - } - return v; -} - -static VALUE -ole_invoke2(VALUE self, VALUE dispid, VALUE args, VALUE types, USHORT dispkind) -{ - HRESULT hr; - struct oledata *pole = NULL; - unsigned int argErr = 0; - EXCEPINFO excepinfo; - VARIANT result; - DISPPARAMS dispParams; - VARIANTARG* realargs = NULL; - int i, j; VALUE obj = Qnil; - VALUE tp, param; - VALUE v; - VARTYPE vt; - - Check_Type(args, T_ARRAY); - Check_Type(types, T_ARRAY); - - memset(&excepinfo, 0, sizeof(EXCEPINFO)); - memset(&dispParams, 0, sizeof(DISPPARAMS)); - VariantInit(&result); - pole = oledata_get_struct(self); - - dispParams.cArgs = RARRAY_LEN(args); - dispParams.rgvarg = ALLOCA_N(VARIANTARG, dispParams.cArgs); - realargs = ALLOCA_N(VARIANTARG, dispParams.cArgs); - for (i = 0, j = dispParams.cArgs - 1; i < (int)dispParams.cArgs; i++, j--) - { - VariantInit(&realargs[i]); - VariantInit(&dispParams.rgvarg[i]); - tp = rb_ary_entry(types, j); - vt = (VARTYPE)RB_FIX2INT(tp); - V_VT(&dispParams.rgvarg[i]) = vt; - param = rb_ary_entry(args, j); - if (param == Qnil) - { - - V_VT(&dispParams.rgvarg[i]) = V_VT(&realargs[i]) = VT_ERROR; - V_ERROR(&dispParams.rgvarg[i]) = V_ERROR(&realargs[i]) = DISP_E_PARAMNOTFOUND; - } - else - { - if (vt & VT_ARRAY) - { - int ent; - LPBYTE pb; - short* ps; - LPLONG pl; - VARIANT* pv; - CY *py; - VARTYPE v; - SAFEARRAYBOUND rgsabound[1]; - Check_Type(param, T_ARRAY); - rgsabound[0].lLbound = 0; - rgsabound[0].cElements = RARRAY_LEN(param); - v = vt & ~(VT_ARRAY | VT_BYREF); - V_ARRAY(&realargs[i]) = SafeArrayCreate(v, 1, rgsabound); - V_VT(&realargs[i]) = VT_ARRAY | v; - SafeArrayLock(V_ARRAY(&realargs[i])); - pb = V_ARRAY(&realargs[i])->pvData; - ps = V_ARRAY(&realargs[i])->pvData; - pl = V_ARRAY(&realargs[i])->pvData; - py = V_ARRAY(&realargs[i])->pvData; - pv = V_ARRAY(&realargs[i])->pvData; - for (ent = 0; ent < (int)rgsabound[0].cElements; ent++) - { - VARIANT velem; - VALUE elem = rb_ary_entry(param, ent); - ole_val2variant(elem, &velem); - if (v != VT_VARIANT) - { - VariantChangeTypeEx(&velem, &velem, - cWIN32OLE_lcid, 0, v); - } - switch (v) - { - /* 128 bits */ - case VT_VARIANT: - *pv++ = velem; - break; - /* 64 bits */ - case VT_R8: - case VT_CY: - case VT_DATE: - *py++ = V_CY(&velem); - break; - /* 16 bits */ - case VT_BOOL: - case VT_I2: - case VT_UI2: - *ps++ = V_I2(&velem); - break; - /* 8 bites */ - case VT_UI1: - case VT_I1: - *pb++ = V_UI1(&velem); - break; - /* 32 bits */ - default: - *pl++ = V_I4(&velem); - break; - } - } - SafeArrayUnlock(V_ARRAY(&realargs[i])); - } - else - { - ole_val2variant(param, &realargs[i]); - if ((vt & (~VT_BYREF)) != VT_VARIANT) - { - hr = VariantChangeTypeEx(&realargs[i], &realargs[i], - cWIN32OLE_lcid, 0, - (VARTYPE)(vt & (~VT_BYREF))); - if (hr != S_OK) - { - rb_raise(rb_eTypeError, "not valid value"); - } - } - } - if ((vt & VT_BYREF) || vt == VT_VARIANT) - { - if (vt == VT_VARIANT) - V_VT(&dispParams.rgvarg[i]) = VT_VARIANT | VT_BYREF; - switch (vt & (~VT_BYREF)) - { - /* 128 bits */ - case VT_VARIANT: - V_VARIANTREF(&dispParams.rgvarg[i]) = &realargs[i]; - break; - /* 64 bits */ - case VT_R8: - case VT_CY: - case VT_DATE: - V_CYREF(&dispParams.rgvarg[i]) = &V_CY(&realargs[i]); - break; - /* 16 bits */ - case VT_BOOL: - case VT_I2: - case VT_UI2: - V_I2REF(&dispParams.rgvarg[i]) = &V_I2(&realargs[i]); - break; - /* 8 bites */ - case VT_UI1: - case VT_I1: - V_UI1REF(&dispParams.rgvarg[i]) = &V_UI1(&realargs[i]); - break; - /* 32 bits */ - default: - V_I4REF(&dispParams.rgvarg[i]) = &V_I4(&realargs[i]); - break; - } - } - else - { - /* copy 64 bits of data */ - V_CY(&dispParams.rgvarg[i]) = V_CY(&realargs[i]); - } - } - } - - if (dispkind & DISPATCH_PROPERTYPUT) { - dispParams.cNamedArgs = 1; - dispParams.rgdispidNamedArgs = ALLOCA_N( DISPID, 1 ); - dispParams.rgdispidNamedArgs[0] = DISPID_PROPERTYPUT; - } - - hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, RB_NUM2INT(dispid), - &IID_NULL, cWIN32OLE_lcid, - dispkind, - &dispParams, &result, - &excepinfo, &argErr); - - if (FAILED(hr)) { - v = ole_excepinfo2msg(&excepinfo); - ole_raise(hr, eWIN32OLERuntimeError, "(in OLE method `<dispatch id:%d>': )%s", - RB_NUM2INT(dispid), - StringValuePtr(v)); - } - - /* clear dispatch parameter */ - if(dispParams.cArgs > 0) { - set_argv(realargs, 0, dispParams.cArgs); - } - - obj = ole_variant2val(&result); - VariantClear(&result); - return obj; -} - -/* - * call-seq: - * WIN32OLE#_invoke(dispid, args, types) - * - * Runs the early binding method. - * The 1st argument specifies dispatch ID, - * the 2nd argument specifies the array of arguments, - * the 3rd argument specifies the array of the type of arguments. - * - * excel = WIN32OLE.new('Excel.Application') - * excel._invoke(302, [], []) # same effect as excel.Quit - */ -static VALUE -fole_invoke2(VALUE self, VALUE dispid, VALUE args, VALUE types) -{ - return ole_invoke2(self, dispid, args, types, DISPATCH_METHOD); -} - -/* - * call-seq: - * WIN32OLE#_getproperty(dispid, args, types) - * - * Runs the early binding method to get property. - * The 1st argument specifies dispatch ID, - * the 2nd argument specifies the array of arguments, - * the 3rd argument specifies the array of the type of arguments. - * - * excel = WIN32OLE.new('Excel.Application') - * puts excel._getproperty(558, [], []) # same effect as puts excel.visible - */ -static VALUE -fole_getproperty2(VALUE self, VALUE dispid, VALUE args, VALUE types) -{ - return ole_invoke2(self, dispid, args, types, DISPATCH_PROPERTYGET); -} - -/* - * call-seq: - * WIN32OLE#_setproperty(dispid, args, types) - * - * Runs the early binding method to set property. - * The 1st argument specifies dispatch ID, - * the 2nd argument specifies the array of arguments, - * the 3rd argument specifies the array of the type of arguments. - * - * excel = WIN32OLE.new('Excel.Application') - * excel._setproperty(558, [true], [WIN32OLE::VARIANT::VT_BOOL]) # same effect as excel.visible = true - */ -static VALUE -fole_setproperty2(VALUE self, VALUE dispid, VALUE args, VALUE types) -{ - return ole_invoke2(self, dispid, args, types, DISPATCH_PROPERTYPUT); -} - -/* - * call-seq: - * WIN32OLE[a1, a2, ...]=val - * - * Sets the value to WIN32OLE object specified by a1, a2, ... - * - * dict = WIN32OLE.new('Scripting.Dictionary') - * dict.add('ruby', 'RUBY') - * dict['ruby'] = 'Ruby' - * puts dict['ruby'] # => 'Ruby' - * - * Remark: You can not use this method to set the property value. - * - * excel = WIN32OLE.new('Excel.Application') - * # excel['Visible'] = true # This is error !!! - * excel.Visible = true # You should to use this style to set the property. - * - */ -static VALUE -fole_setproperty_with_bracket(int argc, VALUE *argv, VALUE self) -{ - VALUE v = ole_invoke(argc, argv, self, DISPATCH_PROPERTYPUT, TRUE); - if (v == rb_eNoMethodError) { - return rb_call_super(argc, argv); - } - return v; -} - -/* - * call-seq: - * WIN32OLE.setproperty('property', [arg1, arg2,...] val) - * - * Sets property of OLE object. - * When you want to set property with argument, you can use this method. - * - * excel = WIN32OLE.new('Excel.Application') - * excel.Visible = true - * book = excel.workbooks.add - * sheet = book.worksheets(1) - * sheet.setproperty('Cells', 1, 2, 10) # => The B1 cell value is 10. - */ -static VALUE -fole_setproperty(int argc, VALUE *argv, VALUE self) -{ - VALUE v = ole_invoke(argc, argv, self, DISPATCH_PROPERTYPUT, FALSE); - if (v == rb_eNoMethodError) { - return rb_call_super(argc, argv); - } - return v; -} - -/* - * call-seq: - * WIN32OLE[a1,a2,...] - * - * Returns the value of Collection specified by a1, a2,.... - * - * dict = WIN32OLE.new('Scripting.Dictionary') - * dict.add('ruby', 'Ruby') - * puts dict['ruby'] # => 'Ruby' (same as `puts dict.item('ruby')') - * - * Remark: You can not use this method to get the property. - * excel = WIN32OLE.new('Excel.Application') - * # puts excel['Visible'] This is error !!! - * puts excel.Visible # You should to use this style to get the property. - * - */ -static VALUE -fole_getproperty_with_bracket(int argc, VALUE *argv, VALUE self) -{ - VALUE v = ole_invoke(argc, argv, self, DISPATCH_PROPERTYGET, TRUE); - if (v == rb_eNoMethodError) { - return rb_call_super(argc, argv); - } - return v; -} - -static VALUE -ole_propertyput(VALUE self, VALUE property, VALUE value) -{ - struct oledata *pole = NULL; - unsigned argErr; - unsigned int index; - HRESULT hr; - EXCEPINFO excepinfo; - DISPID dispID = DISPID_VALUE; - DISPID dispIDParam = DISPID_PROPERTYPUT; - USHORT wFlags = DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF; - DISPPARAMS dispParams; - VARIANTARG propertyValue[2]; - OLECHAR* pBuf[1]; - VALUE v; - LCID lcid = cWIN32OLE_lcid; - dispParams.rgdispidNamedArgs = &dispIDParam; - dispParams.rgvarg = propertyValue; - dispParams.cNamedArgs = 1; - dispParams.cArgs = 1; - - VariantInit(&propertyValue[0]); - VariantInit(&propertyValue[1]); - memset(&excepinfo, 0, sizeof(excepinfo)); - - pole = oledata_get_struct(self); - - /* get ID from property name */ - pBuf[0] = ole_vstr2wc(property); - hr = pole->pDispatch->lpVtbl->GetIDsOfNames(pole->pDispatch, &IID_NULL, - pBuf, 1, lcid, &dispID); - SysFreeString(pBuf[0]); - pBuf[0] = NULL; - - if(FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, - "unknown property or method: `%s'", - StringValuePtr(property)); - } - /* set property value */ - ole_val2variant(value, &propertyValue[0]); - hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, dispID, &IID_NULL, - lcid, wFlags, &dispParams, - NULL, &excepinfo, &argErr); - - for(index = 0; index < dispParams.cArgs; ++index) { - VariantClear(&propertyValue[index]); - } - if (FAILED(hr)) { - v = ole_excepinfo2msg(&excepinfo); - ole_raise(hr, eWIN32OLERuntimeError, "(in setting property `%s': )%s", - StringValuePtr(property), - StringValuePtr(v)); - } - return Qnil; -} - -/* - * call-seq: - * WIN32OLE#ole_free - * - * invokes Release method of Dispatch interface of WIN32OLE object. - * Usually, you do not need to call this method because Release method - * called automatically when WIN32OLE object garbaged. - * - */ -static VALUE -fole_free(VALUE self) -{ - struct oledata *pole = NULL; - pole = oledata_get_struct(self); - OLE_FREE(pole->pDispatch); - pole->pDispatch = NULL; - return Qnil; -} - -static VALUE -ole_each_sub(VALUE pEnumV) -{ - VARIANT variant; - VALUE obj = Qnil; - IEnumVARIANT *pEnum = (IEnumVARIANT *)pEnumV; - VariantInit(&variant); - while(pEnum->lpVtbl->Next(pEnum, 1, &variant, NULL) == S_OK) { - obj = ole_variant2val(&variant); - VariantClear(&variant); - VariantInit(&variant); - rb_yield(obj); - } - return Qnil; -} - -static VALUE -ole_ienum_free(VALUE pEnumV) -{ - IEnumVARIANT *pEnum = (IEnumVARIANT *)pEnumV; - OLE_RELEASE(pEnum); - return Qnil; -} - -/* - * call-seq: - * WIN32OLE#each {|i|...} - * - * Iterates over each item of OLE collection which has IEnumVARIANT interface. - * - * excel = WIN32OLE.new('Excel.Application') - * book = excel.workbooks.add - * sheets = book.worksheets(1) - * cells = sheets.cells("A1:A5") - * cells.each do |cell| - * cell.value = 10 - * end - */ -static VALUE -fole_each(VALUE self) -{ - LCID lcid = cWIN32OLE_lcid; - - struct oledata *pole = NULL; - - unsigned int argErr; - EXCEPINFO excepinfo; - DISPPARAMS dispParams; - VARIANT result; - HRESULT hr; - IEnumVARIANT *pEnum = NULL; - void *p; - - RETURN_ENUMERATOR(self, 0, 0); - - VariantInit(&result); - dispParams.rgvarg = NULL; - dispParams.rgdispidNamedArgs = NULL; - dispParams.cNamedArgs = 0; - dispParams.cArgs = 0; - memset(&excepinfo, 0, sizeof(excepinfo)); - - pole = oledata_get_struct(self); - hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, DISPID_NEWENUM, - &IID_NULL, lcid, - DISPATCH_METHOD | DISPATCH_PROPERTYGET, - &dispParams, &result, - &excepinfo, &argErr); - - if (FAILED(hr)) { - VariantClear(&result); - ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to get IEnum Interface"); - } - - if (V_VT(&result) == VT_UNKNOWN) { - hr = V_UNKNOWN(&result)->lpVtbl->QueryInterface(V_UNKNOWN(&result), - &IID_IEnumVARIANT, - &p); - pEnum = p; - } else if (V_VT(&result) == VT_DISPATCH) { - hr = V_DISPATCH(&result)->lpVtbl->QueryInterface(V_DISPATCH(&result), - &IID_IEnumVARIANT, - &p); - pEnum = p; - } - if (FAILED(hr) || !pEnum) { - VariantClear(&result); - ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to get IEnum Interface"); - } - - VariantClear(&result); - rb_ensure(ole_each_sub, (VALUE)pEnum, ole_ienum_free, (VALUE)pEnum); - return Qnil; -} - -/* - * call-seq: - * WIN32OLE#method_missing(id [,arg1, arg2, ...]) - * - * Calls WIN32OLE#invoke method. - */ -static VALUE -fole_missing(int argc, VALUE *argv, VALUE self) -{ - VALUE mid, org_mid, sym, v; - const char* mname; - long n; - rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS); - mid = org_mid = argv[0]; - sym = rb_check_symbol(&mid); - if (!NIL_P(sym)) mid = rb_sym2str(sym); - mname = StringValueCStr(mid); - if(!mname) { - rb_raise(rb_eRuntimeError, "fail: unknown method or property"); - } - n = RSTRING_LEN(mid); - if(mname[n-1] == '=') { - rb_check_arity(argc, 2, 2); - argv[0] = rb_enc_associate(rb_str_subseq(mid, 0, n-1), cWIN32OLE_enc); - - return ole_propertyput(self, argv[0], argv[1]); - } - else { - argv[0] = rb_enc_associate(rb_str_dup(mid), cWIN32OLE_enc); - v = ole_invoke(argc, argv, self, DISPATCH_METHOD|DISPATCH_PROPERTYGET, FALSE); - if (v == rb_eNoMethodError) { - argv[0] = org_mid; - return rb_call_super(argc, argv); - } - return v; - } -} - -static HRESULT -typeinfo_from_ole(struct oledata *pole, ITypeInfo **ppti) -{ - ITypeInfo *pTypeInfo; - ITypeLib *pTypeLib; - BSTR bstr; - VALUE type; - UINT i; - UINT count; - LCID lcid = cWIN32OLE_lcid; - HRESULT hr = pole->pDispatch->lpVtbl->GetTypeInfo(pole->pDispatch, - 0, lcid, &pTypeInfo); - if(FAILED(hr)) { - ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeInfo"); - } - hr = pTypeInfo->lpVtbl->GetDocumentation(pTypeInfo, - -1, - &bstr, - NULL, NULL, NULL); - type = WC2VSTR(bstr); - hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, &pTypeLib, &i); - OLE_RELEASE(pTypeInfo); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetContainingTypeLib"); - } - count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib); - for (i = 0; i < count; i++) { - hr = pTypeLib->lpVtbl->GetDocumentation(pTypeLib, i, - &bstr, NULL, NULL, NULL); - if (SUCCEEDED(hr) && rb_str_cmp(WC2VSTR(bstr), type) == 0) { - hr = pTypeLib->lpVtbl->GetTypeInfo(pTypeLib, i, &pTypeInfo); - if (SUCCEEDED(hr)) { - *ppti = pTypeInfo; - break; - } - } - } - OLE_RELEASE(pTypeLib); - return hr; -} - -static VALUE -ole_methods(VALUE self, int mask) -{ - ITypeInfo *pTypeInfo; - HRESULT hr; - VALUE methods; - struct oledata *pole = NULL; - - pole = oledata_get_struct(self); - methods = rb_ary_new(); - - hr = typeinfo_from_ole(pole, &pTypeInfo); - if(FAILED(hr)) - return methods; - rb_ary_concat(methods, ole_methods_from_typeinfo(pTypeInfo, mask)); - OLE_RELEASE(pTypeInfo); - return methods; -} - -/* - * call-seq: - * WIN32OLE#ole_methods - * - * Returns the array of WIN32OLE_METHOD object. - * The element is OLE method of WIN32OLE object. - * - * excel = WIN32OLE.new('Excel.Application') - * methods = excel.ole_methods - * - */ -static VALUE -fole_methods(VALUE self) -{ - return ole_methods( self, INVOKE_FUNC | INVOKE_PROPERTYGET | INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF); -} - -/* - * call-seq: - * WIN32OLE#ole_get_methods - * - * Returns the array of WIN32OLE_METHOD object . - * The element of the array is property (gettable) of WIN32OLE object. - * - * excel = WIN32OLE.new('Excel.Application') - * properties = excel.ole_get_methods - */ -static VALUE -fole_get_methods(VALUE self) -{ - return ole_methods( self, INVOKE_PROPERTYGET); -} - -/* - * call-seq: - * WIN32OLE#ole_put_methods - * - * Returns the array of WIN32OLE_METHOD object . - * The element of the array is property (settable) of WIN32OLE object. - * - * excel = WIN32OLE.new('Excel.Application') - * properties = excel.ole_put_methods - */ -static VALUE -fole_put_methods(VALUE self) -{ - return ole_methods( self, INVOKE_PROPERTYPUT|INVOKE_PROPERTYPUTREF); -} - -/* - * call-seq: - * WIN32OLE#ole_func_methods - * - * Returns the array of WIN32OLE_METHOD object . - * The element of the array is property (settable) of WIN32OLE object. - * - * excel = WIN32OLE.new('Excel.Application') - * properties = excel.ole_func_methods - * - */ -static VALUE -fole_func_methods(VALUE self) -{ - return ole_methods( self, INVOKE_FUNC); -} - -/* - * call-seq: - * WIN32OLE#ole_type - * - * Returns WIN32OLE_TYPE object. - * - * excel = WIN32OLE.new('Excel.Application') - * tobj = excel.ole_type - */ -static VALUE -fole_type(VALUE self) -{ - ITypeInfo *pTypeInfo; - HRESULT hr; - struct oledata *pole = NULL; - LCID lcid = cWIN32OLE_lcid; - VALUE type = Qnil; - - pole = oledata_get_struct(self); - - hr = pole->pDispatch->lpVtbl->GetTypeInfo( pole->pDispatch, 0, lcid, &pTypeInfo ); - if(FAILED(hr)) { - ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeInfo"); - } - type = ole_type_from_itypeinfo(pTypeInfo); - OLE_RELEASE(pTypeInfo); - if (type == Qnil) { - rb_raise(rb_eRuntimeError, "failed to create WIN32OLE_TYPE obj from ITypeInfo"); - } - return type; -} - -/* - * call-seq: - * WIN32OLE#ole_typelib -> The WIN32OLE_TYPELIB object - * - * Returns the WIN32OLE_TYPELIB object. The object represents the - * type library which contains the WIN32OLE object. - * - * excel = WIN32OLE.new('Excel.Application') - * tlib = excel.ole_typelib - * puts tlib.name # -> 'Microsoft Excel 9.0 Object Library' - */ -static VALUE -fole_typelib(VALUE self) -{ - struct oledata *pole = NULL; - HRESULT hr; - ITypeInfo *pTypeInfo; - LCID lcid = cWIN32OLE_lcid; - VALUE vtlib = Qnil; - - pole = oledata_get_struct(self); - hr = pole->pDispatch->lpVtbl->GetTypeInfo(pole->pDispatch, - 0, lcid, &pTypeInfo); - if(FAILED(hr)) { - ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeInfo"); - } - vtlib = ole_typelib_from_itypeinfo(pTypeInfo); - OLE_RELEASE(pTypeInfo); - if (vtlib == Qnil) { - rb_raise(rb_eRuntimeError, "failed to get type library info."); - } - return vtlib; -} - -/* - * call-seq: - * WIN32OLE#ole_query_interface(iid) -> WIN32OLE object - * - * Returns WIN32OLE object for a specific dispatch or dual - * interface specified by iid. - * - * ie = WIN32OLE.new('InternetExplorer.Application') - * ie_web_app = ie.ole_query_interface('{0002DF05-0000-0000-C000-000000000046}') # => WIN32OLE object for dispinterface IWebBrowserApp - */ -static VALUE -fole_query_interface(VALUE self, VALUE str_iid) -{ - HRESULT hr; - OLECHAR *pBuf; - IID iid; - struct oledata *pole = NULL; - IDispatch *pDispatch; - void *p; - - pBuf = ole_vstr2wc(str_iid); - hr = CLSIDFromString(pBuf, &iid); - SysFreeString(pBuf); - if(FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, - "invalid iid: `%s'", - StringValuePtr(str_iid)); - } - - pole = oledata_get_struct(self); - if(!pole->pDispatch) { - rb_raise(rb_eRuntimeError, "failed to get dispatch interface"); - } - - hr = pole->pDispatch->lpVtbl->QueryInterface(pole->pDispatch, &iid, - &p); - if(FAILED(hr)) { - ole_raise(hr, eWIN32OLEQueryInterfaceError, - "failed to get interface `%s'", - StringValuePtr(str_iid)); - } - - pDispatch = p; - return create_win32ole_object(cWIN32OLE, pDispatch, 0, 0); -} - -/* - * call-seq: - * WIN32OLE#ole_respond_to?(method) -> true or false - * - * Returns true when OLE object has OLE method, otherwise returns false. - * - * ie = WIN32OLE.new('InternetExplorer.Application') - * ie.ole_respond_to?("gohome") => true - */ -static VALUE -fole_respond_to(VALUE self, VALUE method) -{ - struct oledata *pole = NULL; - BSTR wcmdname; - DISPID DispID; - HRESULT hr; - if(!RB_TYPE_P(method, T_STRING) && !RB_TYPE_P(method, T_SYMBOL)) { - rb_raise(rb_eTypeError, "wrong argument type (expected String or Symbol)"); - } - if (RB_TYPE_P(method, T_SYMBOL)) { - method = rb_sym2str(method); - } - pole = oledata_get_struct(self); - wcmdname = ole_vstr2wc(method); - hr = pole->pDispatch->lpVtbl->GetIDsOfNames( pole->pDispatch, &IID_NULL, - &wcmdname, 1, cWIN32OLE_lcid, &DispID); - SysFreeString(wcmdname); - return SUCCEEDED(hr) ? Qtrue : Qfalse; -} - -HRESULT -ole_docinfo_from_type(ITypeInfo *pTypeInfo, BSTR *name, BSTR *helpstr, DWORD *helpcontext, BSTR *helpfile) -{ - HRESULT hr; - ITypeLib *pTypeLib; - UINT i; - - hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, &pTypeLib, &i); - if (FAILED(hr)) { - return hr; - } - - hr = pTypeLib->lpVtbl->GetDocumentation(pTypeLib, i, - name, helpstr, - helpcontext, helpfile); - if (FAILED(hr)) { - OLE_RELEASE(pTypeLib); - return hr; - } - OLE_RELEASE(pTypeLib); - return hr; -} - -static VALUE -ole_usertype2val(ITypeInfo *pTypeInfo, TYPEDESC *pTypeDesc, VALUE typedetails) -{ - HRESULT hr; - BSTR bstr; - ITypeInfo *pRefTypeInfo; - VALUE type = Qnil; - - hr = pTypeInfo->lpVtbl->GetRefTypeInfo(pTypeInfo, - V_UNION1(pTypeDesc, hreftype), - &pRefTypeInfo); - if(FAILED(hr)) - return Qnil; - hr = ole_docinfo_from_type(pRefTypeInfo, &bstr, NULL, NULL, NULL); - if(FAILED(hr)) { - OLE_RELEASE(pRefTypeInfo); - return Qnil; - } - OLE_RELEASE(pRefTypeInfo); - type = WC2VSTR(bstr); - if(typedetails != Qnil) - rb_ary_push(typedetails, type); - return type; -} - -static VALUE -ole_ptrtype2val(ITypeInfo *pTypeInfo, TYPEDESC *pTypeDesc, VALUE typedetails) -{ - TYPEDESC *p = pTypeDesc; - VALUE type = rb_str_new2(""); - - if (p->vt == VT_PTR || p->vt == VT_SAFEARRAY) { - p = V_UNION1(p, lptdesc); - type = ole_typedesc2val(pTypeInfo, p, typedetails); - } - return type; -} - -VALUE -ole_typedesc2val(ITypeInfo *pTypeInfo, TYPEDESC *pTypeDesc, VALUE typedetails) -{ - VALUE str; - VALUE typestr = Qnil; - switch(pTypeDesc->vt) { - case VT_I2: - typestr = rb_str_new2("I2"); - break; - case VT_I4: - typestr = rb_str_new2("I4"); - break; - case VT_R4: - typestr = rb_str_new2("R4"); - break; - case VT_R8: - typestr = rb_str_new2("R8"); - break; - case VT_CY: - typestr = rb_str_new2("CY"); - break; - case VT_DATE: - typestr = rb_str_new2("DATE"); - break; - case VT_BSTR: - typestr = rb_str_new2("BSTR"); - break; - case VT_BOOL: - typestr = rb_str_new2("BOOL"); - break; - case VT_VARIANT: - typestr = rb_str_new2("VARIANT"); - break; - case VT_DECIMAL: - typestr = rb_str_new2("DECIMAL"); - break; - case VT_I1: - typestr = rb_str_new2("I1"); - break; - case VT_UI1: - typestr = rb_str_new2("UI1"); - break; - case VT_UI2: - typestr = rb_str_new2("UI2"); - break; - case VT_UI4: - typestr = rb_str_new2("UI4"); - break; -#if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) - case VT_I8: - typestr = rb_str_new2("I8"); - break; - case VT_UI8: - typestr = rb_str_new2("UI8"); - break; -#endif - case VT_INT: - typestr = rb_str_new2("INT"); - break; - case VT_UINT: - typestr = rb_str_new2("UINT"); - break; - case VT_VOID: - typestr = rb_str_new2("VOID"); - break; - case VT_HRESULT: - typestr = rb_str_new2("HRESULT"); - break; - case VT_PTR: - typestr = rb_str_new2("PTR"); - if(typedetails != Qnil) - rb_ary_push(typedetails, typestr); - return ole_ptrtype2val(pTypeInfo, pTypeDesc, typedetails); - case VT_SAFEARRAY: - typestr = rb_str_new2("SAFEARRAY"); - if(typedetails != Qnil) - rb_ary_push(typedetails, typestr); - return ole_ptrtype2val(pTypeInfo, pTypeDesc, typedetails); - case VT_CARRAY: - typestr = rb_str_new2("CARRAY"); - break; - case VT_USERDEFINED: - typestr = rb_str_new2("USERDEFINED"); - if (typedetails != Qnil) - rb_ary_push(typedetails, typestr); - str = ole_usertype2val(pTypeInfo, pTypeDesc, typedetails); - if (str != Qnil) { - return str; - } - return typestr; - case VT_UNKNOWN: - typestr = rb_str_new2("UNKNOWN"); - break; - case VT_DISPATCH: - typestr = rb_str_new2("DISPATCH"); - break; - case VT_ERROR: - typestr = rb_str_new2("ERROR"); - break; - case VT_LPWSTR: - typestr = rb_str_new2("LPWSTR"); - break; - case VT_LPSTR: - typestr = rb_str_new2("LPSTR"); - break; - case VT_RECORD: - typestr = rb_str_new2("RECORD"); - break; - default: - typestr = rb_str_new2("Unknown Type "); - rb_str_concat(typestr, rb_fix2str(RB_INT2FIX(pTypeDesc->vt), 10)); - break; - } - if (typedetails != Qnil) - rb_ary_push(typedetails, typestr); - return typestr; -} - -/* - * call-seq: - * WIN32OLE#ole_method_help(method) - * - * Returns WIN32OLE_METHOD object corresponding with method - * specified by 1st argument. - * - * excel = WIN32OLE.new('Excel.Application') - * method = excel.ole_method_help('Quit') - * - */ -static VALUE -fole_method_help(VALUE self, VALUE cmdname) -{ - ITypeInfo *pTypeInfo; - HRESULT hr; - struct oledata *pole = NULL; - VALUE obj; - - SafeStringValue(cmdname); - pole = oledata_get_struct(self); - hr = typeinfo_from_ole(pole, &pTypeInfo); - if(FAILED(hr)) - ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to get ITypeInfo"); - - obj = create_win32ole_method(pTypeInfo, cmdname); - - OLE_RELEASE(pTypeInfo); - if (obj == Qnil) - rb_raise(eWIN32OLERuntimeError, "not found %s", - StringValuePtr(cmdname)); - return obj; -} - -/* - * call-seq: - * WIN32OLE#ole_activex_initialize() -> Qnil - * - * Initialize WIN32OLE object(ActiveX Control) by calling - * IPersistMemory::InitNew. - * - * Before calling OLE method, some kind of the ActiveX controls - * created with MFC should be initialized by calling - * IPersistXXX::InitNew. - * - * If and only if you received the exception "HRESULT error code: - * 0x8000ffff catastrophic failure", try this method before - * invoking any ole_method. - * - * obj = WIN32OLE.new("ProgID_or_GUID_of_ActiveX_Control") - * obj.ole_activex_initialize - * obj.method(...) - * - */ -static VALUE -fole_activex_initialize(VALUE self) -{ - struct oledata *pole = NULL; - IPersistMemory *pPersistMemory; - void *p; - - HRESULT hr = S_OK; - - pole = oledata_get_struct(self); - - hr = pole->pDispatch->lpVtbl->QueryInterface(pole->pDispatch, &IID_IPersistMemory, &p); - pPersistMemory = p; - if (SUCCEEDED(hr)) { - hr = pPersistMemory->lpVtbl->InitNew(pPersistMemory); - OLE_RELEASE(pPersistMemory); - if (SUCCEEDED(hr)) { - return Qnil; - } - } - - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, "fail to initialize ActiveX control"); - } - - return Qnil; -} - -HRESULT -typelib_from_val(VALUE obj, ITypeLib **pTypeLib) -{ - LCID lcid = cWIN32OLE_lcid; - HRESULT hr; - struct oledata *pole = NULL; - unsigned int index; - ITypeInfo *pTypeInfo; - pole = oledata_get_struct(obj); - hr = pole->pDispatch->lpVtbl->GetTypeInfo(pole->pDispatch, - 0, lcid, &pTypeInfo); - if (FAILED(hr)) { - return hr; - } - hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, pTypeLib, &index); - OLE_RELEASE(pTypeInfo); - return hr; -} - -static void -com_hash_free(void *ptr) -{ - st_table *tbl = ptr; - st_free_table(tbl); -} - -static void -com_hash_mark(void *ptr) -{ - st_table *tbl = ptr; - rb_mark_hash(tbl); -} - -static size_t -com_hash_size(const void *ptr) -{ - const st_table *tbl = ptr; - return st_memsize(tbl); -} - -static void -check_nano_server(void) -{ - HKEY hsubkey; - LONG err; - const char * subkey = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Server\\ServerLevels"; - const char * regval = "NanoServer"; - - err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subkey, 0, KEY_READ, &hsubkey); - if (err == ERROR_SUCCESS) { - err = RegQueryValueEx(hsubkey, regval, NULL, NULL, NULL, NULL); - if (err == ERROR_SUCCESS) { - g_running_nano = TRUE; - } - RegCloseKey(hsubkey); - } -} - -LCID cWIN32OLE_lcid; - -void -Init_win32ole(void) -{ - cWIN32OLE_lcid = LOCALE_SYSTEM_DEFAULT; - g_ole_initialized_init(); - check_nano_server(); - - com_vtbl.QueryInterface = QueryInterface; - com_vtbl.AddRef = AddRef; - com_vtbl.Release = Release; - com_vtbl.GetTypeInfoCount = GetTypeInfoCount; - com_vtbl.GetTypeInfo = GetTypeInfo; - com_vtbl.GetIDsOfNames = GetIDsOfNames; - com_vtbl.Invoke = Invoke; - - message_filter.QueryInterface = mf_QueryInterface; - message_filter.AddRef = mf_AddRef; - message_filter.Release = mf_Release; - message_filter.HandleInComingCall = mf_HandleInComingCall; - message_filter.RetryRejectedCall = mf_RetryRejectedCall; - message_filter.MessagePending = mf_MessagePending; - - enc2cp_hash = TypedData_Wrap_Struct(0, &win32ole_hash_datatype, 0); - RTYPEDDATA_DATA(enc2cp_hash) = st_init_numtable(); - rb_gc_register_mark_object(enc2cp_hash); - - com_hash = TypedData_Wrap_Struct(0, &win32ole_hash_datatype, 0); - RTYPEDDATA_DATA(com_hash) = st_init_numtable(); - rb_gc_register_mark_object(com_hash); - - cWIN32OLE = rb_define_class("WIN32OLE", rb_cObject); - - rb_define_alloc_func(cWIN32OLE, fole_s_allocate); - - rb_define_method(cWIN32OLE, "initialize", fole_initialize, -1); - - rb_define_singleton_method(cWIN32OLE, "connect", fole_s_connect, -1); - rb_define_singleton_method(cWIN32OLE, "const_load", fole_s_const_load, -1); - - rb_define_singleton_method(cWIN32OLE, "ole_free", fole_s_free, 1); - rb_define_singleton_method(cWIN32OLE, "ole_reference_count", fole_s_reference_count, 1); - rb_define_singleton_method(cWIN32OLE, "ole_show_help", fole_s_show_help, -1); - rb_define_singleton_method(cWIN32OLE, "codepage", fole_s_get_code_page, 0); - rb_define_singleton_method(cWIN32OLE, "codepage=", fole_s_set_code_page, 1); - rb_define_singleton_method(cWIN32OLE, "locale", fole_s_get_locale, 0); - rb_define_singleton_method(cWIN32OLE, "locale=", fole_s_set_locale, 1); - rb_define_singleton_method(cWIN32OLE, "create_guid", fole_s_create_guid, 0); - rb_define_singleton_method(cWIN32OLE, "ole_initialize", fole_s_ole_initialize, 0); - rb_define_singleton_method(cWIN32OLE, "ole_uninitialize", fole_s_ole_uninitialize, 0); - - rb_define_method(cWIN32OLE, "invoke", fole_invoke, -1); - rb_define_method(cWIN32OLE, "[]", fole_getproperty_with_bracket, -1); - rb_define_method(cWIN32OLE, "_invoke", fole_invoke2, 3); - rb_define_method(cWIN32OLE, "_getproperty", fole_getproperty2, 3); - rb_define_method(cWIN32OLE, "_setproperty", fole_setproperty2, 3); - - /* support propput method that takes an argument */ - rb_define_method(cWIN32OLE, "[]=", fole_setproperty_with_bracket, -1); - - rb_define_method(cWIN32OLE, "ole_free", fole_free, 0); - - rb_define_method(cWIN32OLE, "each", fole_each, 0); - rb_define_method(cWIN32OLE, "method_missing", fole_missing, -1); - - /* support setproperty method much like Perl ;-) */ - rb_define_method(cWIN32OLE, "setproperty", fole_setproperty, -1); - - rb_define_method(cWIN32OLE, "ole_methods", fole_methods, 0); - rb_define_method(cWIN32OLE, "ole_get_methods", fole_get_methods, 0); - rb_define_method(cWIN32OLE, "ole_put_methods", fole_put_methods, 0); - rb_define_method(cWIN32OLE, "ole_func_methods", fole_func_methods, 0); - - rb_define_method(cWIN32OLE, "ole_method", fole_method_help, 1); - rb_define_alias(cWIN32OLE, "ole_method_help", "ole_method"); - rb_define_method(cWIN32OLE, "ole_activex_initialize", fole_activex_initialize, 0); - rb_define_method(cWIN32OLE, "ole_type", fole_type, 0); - rb_define_alias(cWIN32OLE, "ole_obj_help", "ole_type"); - rb_define_method(cWIN32OLE, "ole_typelib", fole_typelib, 0); - rb_define_method(cWIN32OLE, "ole_query_interface", fole_query_interface, 1); - rb_define_method(cWIN32OLE, "ole_respond_to?", fole_respond_to, 1); - - /* Constants definition */ - - /* - * Version string of WIN32OLE. - */ - rb_define_const(cWIN32OLE, "VERSION", rb_str_new2(WIN32OLE_VERSION)); - - /* - * After invoking OLE methods with reference arguments, you can access - * the value of arguments by using ARGV. - * - * If the method of OLE(COM) server written by C#.NET is following: - * - * void calcsum(int a, int b, out int c) { - * c = a + b; - * } - * - * then, the Ruby OLE(COM) client script to retrieve the value of - * argument c after invoking calcsum method is following: - * - * a = 10 - * b = 20 - * c = 0 - * comserver.calcsum(a, b, c) - * p c # => 0 - * p WIN32OLE::ARGV # => [10, 20, 30] - * - * You can use WIN32OLE_VARIANT object to retrieve the value of reference - * arguments instead of referring WIN32OLE::ARGV. - * - */ - rb_define_const(cWIN32OLE, "ARGV", rb_ary_new()); - - /* - * 0: ANSI code page. See WIN32OLE.codepage and WIN32OLE.codepage=. - */ - rb_define_const(cWIN32OLE, "CP_ACP", RB_INT2FIX(CP_ACP)); - - /* - * 1: OEM code page. See WIN32OLE.codepage and WIN32OLE.codepage=. - */ - rb_define_const(cWIN32OLE, "CP_OEMCP", RB_INT2FIX(CP_OEMCP)); - - /* - * 2 - */ - rb_define_const(cWIN32OLE, "CP_MACCP", RB_INT2FIX(CP_MACCP)); - - /* - * 3: current thread ANSI code page. See WIN32OLE.codepage and - * WIN32OLE.codepage=. - */ - rb_define_const(cWIN32OLE, "CP_THREAD_ACP", RB_INT2FIX(CP_THREAD_ACP)); - - /* - * 42: symbol code page. See WIN32OLE.codepage and WIN32OLE.codepage=. - */ - rb_define_const(cWIN32OLE, "CP_SYMBOL", RB_INT2FIX(CP_SYMBOL)); - - /* - * 65000: UTF-7 code page. See WIN32OLE.codepage and WIN32OLE.codepage=. - */ - rb_define_const(cWIN32OLE, "CP_UTF7", RB_INT2FIX(CP_UTF7)); - - /* - * 65001: UTF-8 code page. See WIN32OLE.codepage and WIN32OLE.codepage=. - */ - rb_define_const(cWIN32OLE, "CP_UTF8", RB_INT2FIX(CP_UTF8)); - - /* - * 0x0800: default locale for the operating system. See WIN32OLE.locale - * and WIN32OLE.locale=. - */ - rb_define_const(cWIN32OLE, "LOCALE_SYSTEM_DEFAULT", RB_INT2FIX(LOCALE_SYSTEM_DEFAULT)); - - /* - * 0x0400: default locale for the user or process. See WIN32OLE.locale - * and WIN32OLE.locale=. - */ - rb_define_const(cWIN32OLE, "LOCALE_USER_DEFAULT", RB_INT2FIX(LOCALE_USER_DEFAULT)); - - Init_win32ole_variant_m(); - Init_win32ole_typelib(); - Init_win32ole_type(); - Init_win32ole_variable(); - Init_win32ole_method(); - Init_win32ole_param(); - Init_win32ole_event(); - Init_win32ole_variant(); - Init_win32ole_record(); - Init_win32ole_error(); - - ole_init_cp(); -} diff --git a/ext/win32ole/win32ole.gemspec b/ext/win32ole/win32ole.gemspec deleted file mode 100644 index 9c137a5d70..0000000000 --- a/ext/win32ole/win32ole.gemspec +++ /dev/null @@ -1,32 +0,0 @@ -source_version = ["", "ext/win32ole/"].find do |dir| - begin - break File.open(File.join(__dir__, "#{dir}win32ole.c")) {|f| - f.gets("\n#define WIN32OLE_VERSION ") - f.gets[/\s*"(.+)"/, 1] - } - rescue Errno::ENOENT - end -end - -Gem::Specification.new do |spec| - spec.name = "win32ole" - spec.version = source_version - spec.authors = ["Masaki Suketa"] - spec.email = ["suke@ruby-lang.org"] - - spec.summary = %q{Provides an interface for OLE Automation in Ruby} - spec.description = spec.summary - spec.homepage = "https://github.com/ruby/win32ole" - spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0") - spec.licenses = ["Ruby", "BSD-2-Clause"] - - spec.metadata["homepage_uri"] = spec.homepage - spec.metadata["source_code_uri"] = spec.homepage - - spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } - end - spec.bindir = "exe" - spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } - spec.require_paths = ["lib"] -end diff --git a/ext/win32ole/win32ole.h b/ext/win32ole/win32ole.h deleted file mode 100644 index cd627ef765..0000000000 --- a/ext/win32ole/win32ole.h +++ /dev/null @@ -1,155 +0,0 @@ -#ifndef WIN32OLE_H -#define WIN32OLE_H 1 -#include "ruby/ruby.h" -#include "ruby/st.h" -#include "ruby/encoding.h" - -#define GNUC_OLDER_3_4_4 \ - ((__GNUC__ < 3) || \ - ((__GNUC__ <= 3) && (__GNUC_MINOR__ < 4)) || \ - ((__GNUC__ <= 3) && (__GNUC_MINOR__ <= 4) && (__GNUC_PATCHLEVEL__ <= 4))) - -#if (defined(__GNUC__)) && (GNUC_OLDER_3_4_4) -#ifndef NONAMELESSUNION -#define NONAMELESSUNION 1 -#endif -#endif - -#include <ctype.h> - -#include <windows.h> -#include <ocidl.h> -#include <olectl.h> -#include <ole2.h> -#if defined(HAVE_TYPE_IMULTILANGUAGE2) || defined(HAVE_TYPE_IMULTILANGUAGE) -#include <mlang.h> -#endif -#include <stdlib.h> -#include <math.h> -#ifdef HAVE_STDARG_PROTOTYPES -#include <stdarg.h> -#define va_init_list(a,b) va_start(a,b) -#else -#include <varargs.h> -#define va_init_list(a,b) va_start(a) -#endif -#include <objidl.h> - -#define DOUT fprintf(stderr,"%s(%d)\n", __FILE__, __LINE__) -#define DOUTS(x) fprintf(stderr,"%s(%d):" #x "=%s\n",__FILE__, __LINE__,x) -#define DOUTMSG(x) fprintf(stderr, "%s(%d):" #x "\n",__FILE__, __LINE__) -#define DOUTI(x) fprintf(stderr, "%s(%d):" #x "=%d\n",__FILE__, __LINE__,x) -#define DOUTD(x) fprintf(stderr, "%s(%d):" #x "=%f\n",__FILE__, __LINE__,x) - -#if (defined(__GNUC__)) && (GNUC_OLDER_3_4_4) -#define V_UNION1(X, Y) ((X)->u.Y) -#else -#define V_UNION1(X, Y) ((X)->Y) -#endif - -#if (defined(__GNUC__)) && (GNUC_OLDER_3_4_4) -#undef V_UNION -#define V_UNION(X,Y) ((X)->n1.n2.n3.Y) - -#undef V_VT -#define V_VT(X) ((X)->n1.n2.vt) - -#undef V_BOOL -#define V_BOOL(X) V_UNION(X,boolVal) -#endif - -#ifndef V_I1REF -#define V_I1REF(X) V_UNION(X, pcVal) -#endif - -#ifndef V_UI2REF -#define V_UI2REF(X) V_UNION(X, puiVal) -#endif - -#ifndef V_INT -#define V_INT(X) V_UNION(X, intVal) -#endif - -#ifndef V_INTREF -#define V_INTREF(X) V_UNION(X, pintVal) -#endif - -#ifndef V_UINT -#define V_UINT(X) V_UNION(X, uintVal) -#endif - -#ifndef V_UINTREF -#define V_UINTREF(X) V_UNION(X, puintVal) -#endif - -#ifdef HAVE_LONG_LONG -#define I8_2_NUM LL2NUM -#define UI8_2_NUM ULL2NUM -#define NUM2I8 RB_NUM2LL -#define NUM2UI8 RB_NUM2ULL -#else -#define I8_2_NUM RB_INT2NUM -#define UI8_2_NUM RB_UINT2NUM -#define NUM2I8 RB_NUM2INT -#define NUM2UI8 RB_NUM2UINT -#endif - -#define OLE_ADDREF(X) (X) ? ((X)->lpVtbl->AddRef(X)) : 0 -#define OLE_RELEASE(X) (X) ? ((X)->lpVtbl->Release(X)) : 0 -#define OLE_FREE(x) {\ - if(ole_initialized() == TRUE) {\ - if(x) {\ - OLE_RELEASE(x);\ - (x) = 0;\ - }\ - }\ -} - -#define OLE_GET_TYPEATTR(X, Y) ((X)->lpVtbl->GetTypeAttr((X), (Y))) -#define OLE_RELEASE_TYPEATTR(X, Y) ((X)->lpVtbl->ReleaseTypeAttr((X), (Y))) - -struct oledata { - IDispatch *pDispatch; -}; - -extern VALUE cWIN32OLE; -extern LCID cWIN32OLE_lcid; - -struct oledata *oledata_get_struct(VALUE obj); -LPWSTR ole_vstr2wc(VALUE vstr); -LONG reg_open_key(HKEY hkey, const char *name, HKEY *phkey); -LONG reg_open_vkey(HKEY hkey, VALUE key, HKEY *phkey); -VALUE reg_enum_key(HKEY hkey, DWORD i); -VALUE reg_get_val(HKEY hkey, const char *subkey); -VALUE reg_get_val2(HKEY hkey, const char *subkey); -void ole_initialize(void); -VALUE default_inspect(VALUE self, const char *class_name); -char *ole_wc2mb(LPWSTR pw); -VALUE ole_wc2vstr(LPWSTR pw, BOOL isfree); - -#define WC2VSTR(x) ole_wc2vstr((x), TRUE) - -BOOL ole_initialized(void); -HRESULT ole_docinfo_from_type(ITypeInfo *pTypeInfo, BSTR *name, BSTR *helpstr, DWORD *helpcontext, BSTR *helpfile); -VALUE ole_typedesc2val(ITypeInfo *pTypeInfo, TYPEDESC *pTypeDesc, VALUE typedetails); -VALUE make_inspect(const char *class_name, VALUE detail); -void ole_val2variant(VALUE val, VARIANT *var); -void ole_val2variant2(VALUE val, VARIANT *var); -void ole_val2variant_ex(VALUE val, VARIANT *var, VARTYPE vt); -VALUE ole_variant2val(VARIANT *pvar); -HRESULT ole_val_ary2variant_ary(VALUE val, VARIANT *var, VARTYPE vt); -VOID *val2variant_ptr(VALUE val, VARIANT *var, VARTYPE vt); -HRESULT typelib_from_val(VALUE obj, ITypeLib **pTypeLib); - -#include "win32ole_variant_m.h" -#include "win32ole_typelib.h" -#include "win32ole_type.h" -#include "win32ole_variable.h" -#include "win32ole_method.h" -#include "win32ole_param.h" -#include "win32ole_event.h" -#include "win32ole_variant.h" -#include "win32ole_record.h" -#include "win32ole_error.h" - -#endif diff --git a/ext/win32ole/win32ole_error.c b/ext/win32ole/win32ole_error.c deleted file mode 100644 index 2bb5156263..0000000000 --- a/ext/win32ole/win32ole_error.c +++ /dev/null @@ -1,87 +0,0 @@ -#include "win32ole.h" - -static VALUE ole_hresult2msg(HRESULT hr); - -static VALUE -ole_hresult2msg(HRESULT hr) -{ - VALUE msg = Qnil; - char *p_msg = NULL; - char *term = NULL; - DWORD dwCount; - - char strhr[100]; - sprintf(strhr, " HRESULT error code:0x%08x\n ", (unsigned)hr); - msg = rb_str_new2(strhr); - dwCount = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, hr, - MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), - (LPTSTR)&p_msg, 0, NULL); - if (dwCount == 0) { - dwCount = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, hr, cWIN32OLE_lcid, - (LPTSTR)&p_msg, 0, NULL); - } - if (dwCount > 0) { - term = p_msg + strlen(p_msg); - while (p_msg < term) { - term--; - if (*term == '\r' || *term == '\n') - *term = '\0'; - else break; - } - if (p_msg[0] != '\0') { - rb_str_cat2(msg, p_msg); - } - } - LocalFree(p_msg); - return msg; -} - -void -ole_raise(HRESULT hr, VALUE ecs, const char *fmt, ...) -{ - va_list args; - VALUE msg; - VALUE err_msg; - va_init_list(args, fmt); - msg = rb_vsprintf(fmt, args); - va_end(args); - - err_msg = ole_hresult2msg(hr); - if(err_msg != Qnil) { - rb_str_cat2(msg, "\n"); - rb_str_append(msg, err_msg); - } - rb_exc_raise(rb_exc_new_str(ecs, msg)); -} - -VALUE eWIN32OLERuntimeError; -VALUE eWIN32OLEQueryInterfaceError; - -void -Init_win32ole_error(void) -{ - /* - * Document-class: WIN32OLERuntimeError - * - * Raised when OLE processing failed. - * - * EX: - * - * obj = WIN32OLE.new("NonExistProgID") - * - * raises the exception: - * - * WIN32OLERuntimeError: unknown OLE server: `NonExistProgID' - * HRESULT error code:0x800401f3 - * Invalid class string - * - */ - eWIN32OLERuntimeError = rb_define_class("WIN32OLERuntimeError", rb_eRuntimeError); - eWIN32OLEQueryInterfaceError = rb_define_class("WIN32OLEQueryInterfaceError", eWIN32OLERuntimeError); -} diff --git a/ext/win32ole/win32ole_error.h b/ext/win32ole/win32ole_error.h deleted file mode 100644 index a2f329856f..0000000000 --- a/ext/win32ole/win32ole_error.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef WIN32OLE_ERROR_H -#define WIN32OLE_ERROR_H 1 - -extern VALUE eWIN32OLERuntimeError; -extern VALUE eWIN32OLEQueryInterfaceError; -NORETURN(PRINTF_ARGS(void ole_raise(HRESULT hr, VALUE ecs, const char *fmt, ...), 3, 4)); -void Init_win32ole_error(void); - -#endif diff --git a/ext/win32ole/win32ole_event.c b/ext/win32ole/win32ole_event.c deleted file mode 100644 index 45ebf13433..0000000000 --- a/ext/win32ole/win32ole_event.c +++ /dev/null @@ -1,1278 +0,0 @@ -#include "win32ole.h" - -/* - * Document-class: WIN32OLE_EVENT - * - * <code>WIN32OLE_EVENT</code> objects controls OLE event. - */ - -RUBY_EXTERN void rb_write_error_str(VALUE mesg); - -typedef struct { - struct IEventSinkVtbl * lpVtbl; -} IEventSink, *PEVENTSINK; - -typedef struct IEventSinkVtbl IEventSinkVtbl; - -struct IEventSinkVtbl { - STDMETHOD(QueryInterface)( - PEVENTSINK, - REFIID, - LPVOID *); - STDMETHOD_(ULONG, AddRef)(PEVENTSINK); - STDMETHOD_(ULONG, Release)(PEVENTSINK); - - STDMETHOD(GetTypeInfoCount)( - PEVENTSINK, - UINT *); - STDMETHOD(GetTypeInfo)( - PEVENTSINK, - UINT, - LCID, - ITypeInfo **); - STDMETHOD(GetIDsOfNames)( - PEVENTSINK, - REFIID, - OLECHAR **, - UINT, - LCID, - DISPID *); - STDMETHOD(Invoke)( - PEVENTSINK, - DISPID, - REFIID, - LCID, - WORD, - DISPPARAMS *, - VARIANT *, - EXCEPINFO *, - UINT *); -}; - -typedef struct tagIEVENTSINKOBJ { - const IEventSinkVtbl *lpVtbl; - DWORD m_cRef; - IID m_iid; - long m_event_id; - ITypeInfo *pTypeInfo; -}IEVENTSINKOBJ, *PIEVENTSINKOBJ; - -struct oleeventdata { - DWORD dwCookie; - IConnectionPoint *pConnectionPoint; - IDispatch *pDispatch; - long event_id; -}; - -static VALUE ary_ole_event; -static ID id_events; - -VALUE cWIN32OLE_EVENT; - -STDMETHODIMP EVENTSINK_QueryInterface(PEVENTSINK, REFIID, LPVOID*); -STDMETHODIMP_(ULONG) EVENTSINK_AddRef(PEVENTSINK); -STDMETHODIMP_(ULONG) EVENTSINK_Release(PEVENTSINK); -STDMETHODIMP EVENTSINK_GetTypeInfoCount(PEVENTSINK, UINT*); -STDMETHODIMP EVENTSINK_GetTypeInfo(PEVENTSINK, UINT, LCID, ITypeInfo**); -STDMETHODIMP EVENTSINK_GetIDsOfNames(PEVENTSINK, REFIID, OLECHAR**, UINT, LCID, DISPID*); -STDMETHODIMP EVENTSINK_Invoke(PEVENTSINK, DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*, EXCEPINFO*, UINT*); - -static const IEventSinkVtbl vtEventSink = { - EVENTSINK_QueryInterface, - EVENTSINK_AddRef, - EVENTSINK_Release, - EVENTSINK_GetTypeInfoCount, - EVENTSINK_GetTypeInfo, - EVENTSINK_GetIDsOfNames, - EVENTSINK_Invoke, -}; - -void EVENTSINK_Destructor(PIEVENTSINKOBJ); -static void ole_val2ptr_variant(VALUE val, VARIANT *var); -static void hash2ptr_dispparams(VALUE hash, ITypeInfo *pTypeInfo, DISPID dispid, DISPPARAMS *pdispparams); -static VALUE hash2result(VALUE hash); -static void ary2ptr_dispparams(VALUE ary, DISPPARAMS *pdispparams); -static VALUE exec_callback(VALUE arg); -static VALUE rescue_callback(VALUE arg); -static HRESULT find_iid(VALUE ole, char *pitf, IID *piid, ITypeInfo **ppTypeInfo); -static HRESULT find_coclass(ITypeInfo *pTypeInfo, TYPEATTR *pTypeAttr, ITypeInfo **pTypeInfo2, TYPEATTR **pTypeAttr2); -static HRESULT find_default_source_from_typeinfo(ITypeInfo *pTypeInfo, TYPEATTR *pTypeAttr, ITypeInfo **ppTypeInfo); -static HRESULT find_default_source(VALUE ole, IID *piid, ITypeInfo **ppTypeInfo); -static long ole_search_event_at(VALUE ary, VALUE ev); -static VALUE ole_search_event(VALUE ary, VALUE ev, BOOL *is_default); -static VALUE ole_search_handler_method(VALUE handler, VALUE ev, BOOL *is_default_handler); -static void ole_delete_event(VALUE ary, VALUE ev); -static void oleevent_free(void *ptr); -static size_t oleevent_size(const void *ptr); -static VALUE fev_s_allocate(VALUE klass); -static VALUE ev_advise(int argc, VALUE *argv, VALUE self); -static VALUE fev_initialize(int argc, VALUE *argv, VALUE self); -static void ole_msg_loop(void); -static VALUE fev_s_msg_loop(VALUE klass); -static void add_event_call_back(VALUE obj, VALUE event, VALUE data); -static VALUE ev_on_event(int argc, VALUE *argv, VALUE self, VALUE is_ary_arg); -static VALUE fev_on_event(int argc, VALUE *argv, VALUE self); -static VALUE fev_on_event_with_outargs(int argc, VALUE *argv, VALUE self); -static VALUE fev_off_event(int argc, VALUE *argv, VALUE self); -static VALUE fev_unadvise(VALUE self); -static VALUE fev_set_handler(VALUE self, VALUE val); -static VALUE fev_get_handler(VALUE self); -static VALUE evs_push(VALUE ev); -static VALUE evs_delete(long i); -static VALUE evs_entry(long i); -static long evs_length(void); - - -static const rb_data_type_t oleevent_datatype = { - "win32ole_event", - {NULL, oleevent_free, oleevent_size,}, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY -}; - -STDMETHODIMP EVENTSINK_Invoke( - PEVENTSINK pEventSink, - DISPID dispid, - REFIID riid, - LCID lcid, - WORD wFlags, - DISPPARAMS *pdispparams, - VARIANT *pvarResult, - EXCEPINFO *pexcepinfo, - UINT *puArgErr - ) { - - HRESULT hr; - BSTR bstr; - unsigned int count; - unsigned int i; - ITypeInfo *pTypeInfo; - VARIANT *pvar; - VALUE ary, obj, event, args, outargv, ev, result; - VALUE handler = Qnil; - VALUE arg[3]; - VALUE mid; - VALUE is_outarg = Qfalse; - BOOL is_default_handler = FALSE; - int state; - - PIEVENTSINKOBJ pEV = (PIEVENTSINKOBJ)pEventSink; - pTypeInfo = pEV->pTypeInfo; - obj = evs_entry(pEV->m_event_id); - if (!rb_obj_is_kind_of(obj, cWIN32OLE_EVENT)) { - return NOERROR; - } - - ary = rb_ivar_get(obj, id_events); - if (NIL_P(ary) || !RB_TYPE_P(ary, T_ARRAY)) { - return NOERROR; - } - hr = pTypeInfo->lpVtbl->GetNames(pTypeInfo, dispid, - &bstr, 1, &count); - if (FAILED(hr)) { - return NOERROR; - } - ev = WC2VSTR(bstr); - event = ole_search_event(ary, ev, &is_default_handler); - if (RB_TYPE_P(event, T_ARRAY)) { - handler = rb_ary_entry(event, 0); - mid = rb_intern("call"); - is_outarg = rb_ary_entry(event, 3); - } else { - handler = rb_ivar_get(obj, rb_intern("handler")); - if (handler == Qnil) { - return NOERROR; - } - mid = ole_search_handler_method(handler, ev, &is_default_handler); - } - if (handler == Qnil || mid == Qnil) { - return NOERROR; - } - - args = rb_ary_new(); - if (is_default_handler) { - rb_ary_push(args, ev); - } - - /* make argument of event handler */ - for (i = 0; i < pdispparams->cArgs; ++i) { - pvar = &pdispparams->rgvarg[pdispparams->cArgs-i-1]; - rb_ary_push(args, ole_variant2val(pvar)); - } - outargv = Qnil; - if (is_outarg == Qtrue) { - outargv = rb_ary_new(); - rb_ary_push(args, outargv); - } - - /* - * if exception raised in event callback, - * then you receive cfp consistency error. - * to avoid this error we use begin rescue end. - * and the exception raised then error message print - * and exit ruby process by Win32OLE itself. - */ - arg[0] = handler; - arg[1] = mid; - arg[2] = args; - result = rb_protect(exec_callback, (VALUE)arg, &state); - if (state != 0) { - rescue_callback(Qnil); - } - if(RB_TYPE_P(result, T_HASH)) { - hash2ptr_dispparams(result, pTypeInfo, dispid, pdispparams); - result = hash2result(result); - }else if (is_outarg == Qtrue && RB_TYPE_P(outargv, T_ARRAY)) { - ary2ptr_dispparams(outargv, pdispparams); - } - - if (pvarResult) { - VariantInit(pvarResult); - ole_val2variant(result, pvarResult); - } - - return NOERROR; -} - -STDMETHODIMP -EVENTSINK_QueryInterface( - PEVENTSINK pEV, - REFIID iid, - LPVOID* ppv - ) { - if (IsEqualIID(iid, &IID_IUnknown) || - IsEqualIID(iid, &IID_IDispatch) || - IsEqualIID(iid, &((PIEVENTSINKOBJ)pEV)->m_iid)) { - *ppv = pEV; - } - else { - *ppv = NULL; - return E_NOINTERFACE; - } - ((LPUNKNOWN)*ppv)->lpVtbl->AddRef((LPUNKNOWN)*ppv); - return NOERROR; -} - -STDMETHODIMP_(ULONG) -EVENTSINK_AddRef( - PEVENTSINK pEV - ){ - PIEVENTSINKOBJ pEVObj = (PIEVENTSINKOBJ)pEV; - return ++pEVObj->m_cRef; -} - -STDMETHODIMP_(ULONG) EVENTSINK_Release( - PEVENTSINK pEV - ) { - PIEVENTSINKOBJ pEVObj = (PIEVENTSINKOBJ)pEV; - --pEVObj->m_cRef; - if(pEVObj->m_cRef != 0) - return pEVObj->m_cRef; - EVENTSINK_Destructor(pEVObj); - return 0; -} - -STDMETHODIMP EVENTSINK_GetTypeInfoCount( - PEVENTSINK pEV, - UINT *pct - ) { - *pct = 0; - return NOERROR; -} - -STDMETHODIMP EVENTSINK_GetTypeInfo( - PEVENTSINK pEV, - UINT info, - LCID lcid, - ITypeInfo **pInfo - ) { - *pInfo = NULL; - return DISP_E_BADINDEX; -} - -STDMETHODIMP EVENTSINK_GetIDsOfNames( - PEVENTSINK pEventSink, - REFIID riid, - OLECHAR **szNames, - UINT cNames, - LCID lcid, - DISPID *pDispID - ) { - ITypeInfo *pTypeInfo; - PIEVENTSINKOBJ pEV = (PIEVENTSINKOBJ)pEventSink; - pTypeInfo = pEV->pTypeInfo; - if (pTypeInfo) { - return pTypeInfo->lpVtbl->GetIDsOfNames(pTypeInfo, szNames, cNames, pDispID); - } - return DISP_E_UNKNOWNNAME; -} - -PIEVENTSINKOBJ -EVENTSINK_Constructor(void) -{ - PIEVENTSINKOBJ pEv; - pEv = ALLOC_N(IEVENTSINKOBJ, 1); - if(pEv == NULL) return NULL; - pEv->lpVtbl = &vtEventSink; - pEv->m_cRef = 0; - pEv->m_event_id = 0; - pEv->pTypeInfo = NULL; - return pEv; -} - -void -EVENTSINK_Destructor( - PIEVENTSINKOBJ pEVObj - ) { - if(pEVObj != NULL) { - OLE_RELEASE(pEVObj->pTypeInfo); - free(pEVObj); - pEVObj = NULL; - } -} - -static void -ole_val2ptr_variant(VALUE val, VARIANT *var) -{ - switch (TYPE(val)) { - case T_STRING: - if (V_VT(var) == (VT_BSTR | VT_BYREF)) { - *V_BSTRREF(var) = ole_vstr2wc(val); - } - break; - case T_FIXNUM: - switch(V_VT(var)) { - case (VT_UI1 | VT_BYREF) : - *V_UI1REF(var) = RB_NUM2CHR(val); - break; - case (VT_I2 | VT_BYREF) : - *V_I2REF(var) = (short)RB_NUM2INT(val); - break; - case (VT_I4 | VT_BYREF) : - *V_I4REF(var) = RB_NUM2INT(val); - break; - case (VT_R4 | VT_BYREF) : - *V_R4REF(var) = (float)RB_NUM2INT(val); - break; - case (VT_R8 | VT_BYREF) : - *V_R8REF(var) = RB_NUM2INT(val); - break; - default: - break; - } - break; - case T_FLOAT: - switch(V_VT(var)) { - case (VT_I2 | VT_BYREF) : - *V_I2REF(var) = (short)RB_NUM2INT(val); - break; - case (VT_I4 | VT_BYREF) : - *V_I4REF(var) = RB_NUM2INT(val); - break; - case (VT_R4 | VT_BYREF) : - *V_R4REF(var) = (float)NUM2DBL(val); - break; - case (VT_R8 | VT_BYREF) : - *V_R8REF(var) = NUM2DBL(val); - break; - default: - break; - } - break; - case T_BIGNUM: - if (V_VT(var) == (VT_R8 | VT_BYREF)) { - *V_R8REF(var) = rb_big2dbl(val); - } - break; - case T_TRUE: - if (V_VT(var) == (VT_BOOL | VT_BYREF)) { - *V_BOOLREF(var) = VARIANT_TRUE; - } - break; - case T_FALSE: - if (V_VT(var) == (VT_BOOL | VT_BYREF)) { - *V_BOOLREF(var) = VARIANT_FALSE; - } - break; - default: - break; - } -} - -static void -hash2ptr_dispparams(VALUE hash, ITypeInfo *pTypeInfo, DISPID dispid, DISPPARAMS *pdispparams) -{ - BSTR *bstrs; - HRESULT hr; - UINT len, i; - VARIANT *pvar; - VALUE val; - VALUE key; - len = 0; - bstrs = ALLOCA_N(BSTR, pdispparams->cArgs + 1); - hr = pTypeInfo->lpVtbl->GetNames(pTypeInfo, dispid, - bstrs, pdispparams->cArgs + 1, - &len); - if (FAILED(hr)) - return; - - for (i = 0; i < len - 1; i++) { - key = WC2VSTR(bstrs[i + 1]); - val = rb_hash_aref(hash, RB_UINT2NUM(i)); - if (val == Qnil) - val = rb_hash_aref(hash, key); - if (val == Qnil) - val = rb_hash_aref(hash, rb_str_intern(key)); - pvar = &pdispparams->rgvarg[pdispparams->cArgs-i-1]; - ole_val2ptr_variant(val, pvar); - } -} - -static VALUE -hash2result(VALUE hash) -{ - VALUE ret = Qnil; - ret = rb_hash_aref(hash, rb_str_new2("return")); - if (ret == Qnil) - ret = rb_hash_aref(hash, rb_str_intern(rb_str_new2("return"))); - return ret; -} - -static void -ary2ptr_dispparams(VALUE ary, DISPPARAMS *pdispparams) -{ - int i; - VALUE v; - VARIANT *pvar; - for(i = 0; i < RARRAY_LEN(ary) && (unsigned int) i < pdispparams->cArgs; i++) { - v = rb_ary_entry(ary, i); - pvar = &pdispparams->rgvarg[pdispparams->cArgs-i-1]; - ole_val2ptr_variant(v, pvar); - } -} - -static VALUE -exec_callback(VALUE arg) -{ - VALUE *parg = (VALUE *)arg; - VALUE handler = parg[0]; - VALUE mid = parg[1]; - VALUE args = parg[2]; - return rb_apply(handler, mid, args); -} - -static VALUE -rescue_callback(VALUE arg) -{ - - VALUE error; - VALUE e = rb_errinfo(); - VALUE bt = rb_funcall(e, rb_intern("backtrace"), 0); - VALUE msg = rb_funcall(e, rb_intern("message"), 0); - bt = rb_ary_entry(bt, 0); - error = rb_sprintf("%"PRIsVALUE": %"PRIsVALUE" (%s)\n", bt, msg, rb_obj_classname(e)); - rb_write_error_str(error); - rb_backtrace(); - ruby_finalize(); - exit(-1); - - return Qnil; -} - -static HRESULT -find_iid(VALUE ole, char *pitf, IID *piid, ITypeInfo **ppTypeInfo) -{ - HRESULT hr; - IDispatch *pDispatch; - ITypeInfo *pTypeInfo; - ITypeLib *pTypeLib; - TYPEATTR *pTypeAttr; - HREFTYPE RefType; - ITypeInfo *pImplTypeInfo; - TYPEATTR *pImplTypeAttr; - - struct oledata *pole = NULL; - unsigned int index; - unsigned int count; - int type; - BSTR bstr; - char *pstr; - - BOOL is_found = FALSE; - LCID lcid = cWIN32OLE_lcid; - - pole = oledata_get_struct(ole); - - pDispatch = pole->pDispatch; - - hr = pDispatch->lpVtbl->GetTypeInfo(pDispatch, 0, lcid, &pTypeInfo); - if (FAILED(hr)) - return hr; - - hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, - &pTypeLib, - &index); - OLE_RELEASE(pTypeInfo); - if (FAILED(hr)) - return hr; - - if (!pitf) { - hr = pTypeLib->lpVtbl->GetTypeInfoOfGuid(pTypeLib, - piid, - ppTypeInfo); - OLE_RELEASE(pTypeLib); - return hr; - } - count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib); - for (index = 0; index < count; index++) { - hr = pTypeLib->lpVtbl->GetTypeInfo(pTypeLib, - index, - &pTypeInfo); - if (FAILED(hr)) - break; - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - - if(FAILED(hr)) { - OLE_RELEASE(pTypeInfo); - break; - } - if(pTypeAttr->typekind == TKIND_COCLASS) { - for (type = 0; type < pTypeAttr->cImplTypes; type++) { - hr = pTypeInfo->lpVtbl->GetRefTypeOfImplType(pTypeInfo, - type, - &RefType); - if (FAILED(hr)) - break; - hr = pTypeInfo->lpVtbl->GetRefTypeInfo(pTypeInfo, - RefType, - &pImplTypeInfo); - if (FAILED(hr)) - break; - - hr = pImplTypeInfo->lpVtbl->GetDocumentation(pImplTypeInfo, - -1, - &bstr, - NULL, NULL, NULL); - if (FAILED(hr)) { - OLE_RELEASE(pImplTypeInfo); - break; - } - pstr = ole_wc2mb(bstr); - if (strcmp(pitf, pstr) == 0) { - hr = pImplTypeInfo->lpVtbl->GetTypeAttr(pImplTypeInfo, - &pImplTypeAttr); - if (SUCCEEDED(hr)) { - is_found = TRUE; - *piid = pImplTypeAttr->guid; - if (ppTypeInfo) { - *ppTypeInfo = pImplTypeInfo; - (*ppTypeInfo)->lpVtbl->AddRef((*ppTypeInfo)); - } - pImplTypeInfo->lpVtbl->ReleaseTypeAttr(pImplTypeInfo, - pImplTypeAttr); - } - } - free(pstr); - OLE_RELEASE(pImplTypeInfo); - if (is_found || FAILED(hr)) - break; - } - } - - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - OLE_RELEASE(pTypeInfo); - if (is_found || FAILED(hr)) - break; - } - OLE_RELEASE(pTypeLib); - if(!is_found) - return E_NOINTERFACE; - return hr; -} - -static HRESULT -find_coclass( - ITypeInfo *pTypeInfo, - TYPEATTR *pTypeAttr, - ITypeInfo **pCOTypeInfo, - TYPEATTR **pCOTypeAttr) -{ - HRESULT hr = E_NOINTERFACE; - ITypeLib *pTypeLib; - int count; - BOOL found = FALSE; - ITypeInfo *pTypeInfo2; - TYPEATTR *pTypeAttr2; - int flags; - int i,j; - HREFTYPE href; - ITypeInfo *pRefTypeInfo; - TYPEATTR *pRefTypeAttr; - - hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, &pTypeLib, NULL); - if (FAILED(hr)) { - return hr; - } - count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib); - for (i = 0; i < count && !found; i++) { - hr = pTypeLib->lpVtbl->GetTypeInfo(pTypeLib, i, &pTypeInfo2); - if (FAILED(hr)) - continue; - hr = OLE_GET_TYPEATTR(pTypeInfo2, &pTypeAttr2); - if (FAILED(hr)) { - OLE_RELEASE(pTypeInfo2); - continue; - } - if (pTypeAttr2->typekind != TKIND_COCLASS) { - OLE_RELEASE_TYPEATTR(pTypeInfo2, pTypeAttr2); - OLE_RELEASE(pTypeInfo2); - continue; - } - for (j = 0; j < pTypeAttr2->cImplTypes && !found; j++) { - hr = pTypeInfo2->lpVtbl->GetImplTypeFlags(pTypeInfo2, j, &flags); - if (FAILED(hr)) - continue; - if (!(flags & IMPLTYPEFLAG_FDEFAULT)) - continue; - hr = pTypeInfo2->lpVtbl->GetRefTypeOfImplType(pTypeInfo2, j, &href); - if (FAILED(hr)) - continue; - hr = pTypeInfo2->lpVtbl->GetRefTypeInfo(pTypeInfo2, href, &pRefTypeInfo); - if (FAILED(hr)) - continue; - hr = OLE_GET_TYPEATTR(pRefTypeInfo, &pRefTypeAttr); - if (FAILED(hr)) { - OLE_RELEASE(pRefTypeInfo); - continue; - } - if (IsEqualGUID(&(pTypeAttr->guid), &(pRefTypeAttr->guid))) { - found = TRUE; - } - } - if (!found) { - OLE_RELEASE_TYPEATTR(pTypeInfo2, pTypeAttr2); - OLE_RELEASE(pTypeInfo2); - } - } - OLE_RELEASE(pTypeLib); - if (found) { - *pCOTypeInfo = pTypeInfo2; - *pCOTypeAttr = pTypeAttr2; - hr = S_OK; - } else { - hr = E_NOINTERFACE; - } - return hr; -} - -static HRESULT -find_default_source_from_typeinfo( - ITypeInfo *pTypeInfo, - TYPEATTR *pTypeAttr, - ITypeInfo **ppTypeInfo) -{ - int i = 0; - HRESULT hr = E_NOINTERFACE; - int flags; - HREFTYPE hRefType; - /* Enumerate all implemented types of the COCLASS */ - for (i = 0; i < pTypeAttr->cImplTypes; i++) { - hr = pTypeInfo->lpVtbl->GetImplTypeFlags(pTypeInfo, i, &flags); - if (FAILED(hr)) - continue; - - /* - looking for the [default] [source] - we just hope that it is a dispinterface :-) - */ - if ((flags & IMPLTYPEFLAG_FDEFAULT) && - (flags & IMPLTYPEFLAG_FSOURCE)) { - - hr = pTypeInfo->lpVtbl->GetRefTypeOfImplType(pTypeInfo, - i, &hRefType); - if (FAILED(hr)) - continue; - hr = pTypeInfo->lpVtbl->GetRefTypeInfo(pTypeInfo, - hRefType, ppTypeInfo); - if (SUCCEEDED(hr)) - break; - } - } - return hr; -} - -static HRESULT -find_default_source(VALUE ole, IID *piid, ITypeInfo **ppTypeInfo) -{ - HRESULT hr; - IProvideClassInfo2 *pProvideClassInfo2; - IProvideClassInfo *pProvideClassInfo; - void *p; - - IDispatch *pDispatch; - ITypeInfo *pTypeInfo; - ITypeInfo *pTypeInfo2 = NULL; - TYPEATTR *pTypeAttr; - TYPEATTR *pTypeAttr2 = NULL; - - struct oledata *pole = NULL; - - pole = oledata_get_struct(ole); - pDispatch = pole->pDispatch; - hr = pDispatch->lpVtbl->QueryInterface(pDispatch, - &IID_IProvideClassInfo2, - &p); - if (SUCCEEDED(hr)) { - pProvideClassInfo2 = p; - hr = pProvideClassInfo2->lpVtbl->GetGUID(pProvideClassInfo2, - GUIDKIND_DEFAULT_SOURCE_DISP_IID, - piid); - OLE_RELEASE(pProvideClassInfo2); - if (SUCCEEDED(hr)) { - hr = find_iid(ole, NULL, piid, ppTypeInfo); - } - } - if (SUCCEEDED(hr)) { - return hr; - } - hr = pDispatch->lpVtbl->QueryInterface(pDispatch, - &IID_IProvideClassInfo, - &p); - if (SUCCEEDED(hr)) { - pProvideClassInfo = p; - hr = pProvideClassInfo->lpVtbl->GetClassInfo(pProvideClassInfo, - &pTypeInfo); - OLE_RELEASE(pProvideClassInfo); - } - if (FAILED(hr)) { - hr = pDispatch->lpVtbl->GetTypeInfo(pDispatch, 0, cWIN32OLE_lcid, &pTypeInfo ); - } - if (FAILED(hr)) - return hr; - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if (FAILED(hr)) { - OLE_RELEASE(pTypeInfo); - return hr; - } - - *ppTypeInfo = 0; - hr = find_default_source_from_typeinfo(pTypeInfo, pTypeAttr, ppTypeInfo); - if (!*ppTypeInfo) { - hr = find_coclass(pTypeInfo, pTypeAttr, &pTypeInfo2, &pTypeAttr2); - if (SUCCEEDED(hr)) { - hr = find_default_source_from_typeinfo(pTypeInfo2, pTypeAttr2, ppTypeInfo); - OLE_RELEASE_TYPEATTR(pTypeInfo2, pTypeAttr2); - OLE_RELEASE(pTypeInfo2); - } - } - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - OLE_RELEASE(pTypeInfo); - /* Now that would be a bad surprise, if we didn't find it, wouldn't it? */ - if (!*ppTypeInfo) { - if (SUCCEEDED(hr)) - hr = E_UNEXPECTED; - return hr; - } - - /* Determine IID of default source interface */ - hr = (*ppTypeInfo)->lpVtbl->GetTypeAttr(*ppTypeInfo, &pTypeAttr); - if (SUCCEEDED(hr)) { - *piid = pTypeAttr->guid; - (*ppTypeInfo)->lpVtbl->ReleaseTypeAttr(*ppTypeInfo, pTypeAttr); - } - else - OLE_RELEASE(*ppTypeInfo); - - return hr; -} - -static long -ole_search_event_at(VALUE ary, VALUE ev) -{ - VALUE event; - VALUE event_name; - long i, len; - long ret = -1; - len = RARRAY_LEN(ary); - for(i = 0; i < len; i++) { - event = rb_ary_entry(ary, i); - event_name = rb_ary_entry(event, 1); - if(NIL_P(event_name) && NIL_P(ev)) { - ret = i; - break; - } - else if (RB_TYPE_P(ev, T_STRING) && - RB_TYPE_P(event_name, T_STRING) && - rb_str_cmp(ev, event_name) == 0) { - ret = i; - break; - } - } - return ret; -} - -static VALUE -ole_search_event(VALUE ary, VALUE ev, BOOL *is_default) -{ - VALUE event; - VALUE def_event; - VALUE event_name; - int i, len; - *is_default = FALSE; - def_event = Qnil; - len = RARRAY_LEN(ary); - for(i = 0; i < len; i++) { - event = rb_ary_entry(ary, i); - event_name = rb_ary_entry(event, 1); - if(NIL_P(event_name)) { - *is_default = TRUE; - def_event = event; - } - else if (rb_str_cmp(ev, event_name) == 0) { - *is_default = FALSE; - return event; - } - } - return def_event; -} - -static VALUE -ole_search_handler_method(VALUE handler, VALUE ev, BOOL *is_default_handler) -{ - VALUE mid; - - *is_default_handler = FALSE; - mid = rb_to_id(rb_sprintf("on%"PRIsVALUE, ev)); - if (rb_respond_to(handler, mid)) { - return mid; - } - mid = rb_intern("method_missing"); - if (rb_respond_to(handler, mid)) { - *is_default_handler = TRUE; - return mid; - } - return Qnil; -} - -static void -ole_delete_event(VALUE ary, VALUE ev) -{ - long at = -1; - at = ole_search_event_at(ary, ev); - if (at >= 0) { - rb_ary_delete_at(ary, at); - } -} - - -static void -oleevent_free(void *ptr) -{ - struct oleeventdata *poleev = ptr; - if (poleev->pConnectionPoint) { - poleev->pConnectionPoint->lpVtbl->Unadvise(poleev->pConnectionPoint, poleev->dwCookie); - OLE_RELEASE(poleev->pConnectionPoint); - poleev->pConnectionPoint = NULL; - } - OLE_RELEASE(poleev->pDispatch); - free(poleev); -} - -static size_t -oleevent_size(const void *ptr) -{ - return ptr ? sizeof(struct oleeventdata) : 0; -} - -static VALUE -fev_s_allocate(VALUE klass) -{ - VALUE obj; - struct oleeventdata *poleev; - obj = TypedData_Make_Struct(klass, struct oleeventdata, &oleevent_datatype, poleev); - poleev->dwCookie = 0; - poleev->pConnectionPoint = NULL; - poleev->event_id = 0; - poleev->pDispatch = NULL; - return obj; -} - -static VALUE -ev_advise(int argc, VALUE *argv, VALUE self) -{ - - VALUE ole, itf; - struct oledata *pole = NULL; - char *pitf; - HRESULT hr; - IID iid; - ITypeInfo *pTypeInfo = 0; - IDispatch *pDispatch; - IConnectionPointContainer *pContainer; - IConnectionPoint *pConnectionPoint; - IEVENTSINKOBJ *pIEV; - DWORD dwCookie; - struct oleeventdata *poleev; - void *p; - - rb_scan_args(argc, argv, "11", &ole, &itf); - - if (!rb_obj_is_kind_of(ole, cWIN32OLE)) { - rb_raise(rb_eTypeError, "1st parameter must be WIN32OLE object"); - } - - if(!RB_TYPE_P(itf, T_NIL)) { - pitf = StringValuePtr(itf); - hr = find_iid(ole, pitf, &iid, &pTypeInfo); - } - else { - hr = find_default_source(ole, &iid, &pTypeInfo); - } - if (FAILED(hr)) { - ole_raise(hr, rb_eRuntimeError, "interface not found"); - } - - pole = oledata_get_struct(ole); - pDispatch = pole->pDispatch; - hr = pDispatch->lpVtbl->QueryInterface(pDispatch, - &IID_IConnectionPointContainer, - &p); - if (FAILED(hr)) { - OLE_RELEASE(pTypeInfo); - ole_raise(hr, eWIN32OLEQueryInterfaceError, - "failed to query IConnectionPointContainer"); - } - pContainer = p; - - hr = pContainer->lpVtbl->FindConnectionPoint(pContainer, - &iid, - &pConnectionPoint); - OLE_RELEASE(pContainer); - if (FAILED(hr)) { - OLE_RELEASE(pTypeInfo); - ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to query IConnectionPoint"); - } - pIEV = EVENTSINK_Constructor(); - pIEV->m_iid = iid; - hr = pConnectionPoint->lpVtbl->Advise(pConnectionPoint, - (IUnknown*)pIEV, - &dwCookie); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLEQueryInterfaceError, "Advise Error"); - } - - TypedData_Get_Struct(self, struct oleeventdata, &oleevent_datatype, poleev); - pIEV->m_event_id = evs_length(); - pIEV->pTypeInfo = pTypeInfo; - poleev->dwCookie = dwCookie; - poleev->pConnectionPoint = pConnectionPoint; - poleev->event_id = pIEV->m_event_id; - poleev->pDispatch = pDispatch; - OLE_ADDREF(pDispatch); - - return self; -} - -/* - * call-seq: - * WIN32OLE_EVENT.new(ole, event) #=> WIN32OLE_EVENT object. - * - * Returns OLE event object. - * The first argument specifies WIN32OLE object. - * The second argument specifies OLE event name. - * ie = WIN32OLE.new('InternetExplorer.Application') - * ev = WIN32OLE_EVENT.new(ie, 'DWebBrowserEvents') - */ -static VALUE -fev_initialize(int argc, VALUE *argv, VALUE self) -{ - ev_advise(argc, argv, self); - evs_push(self); - rb_ivar_set(self, id_events, rb_ary_new()); - fev_set_handler(self, Qnil); - return self; -} - -static void -ole_msg_loop(void) -{ - MSG msg; - while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } -} - -/* - * call-seq: - * WIN32OLE_EVENT.message_loop - * - * Translates and dispatches Windows message. - */ -static VALUE -fev_s_msg_loop(VALUE klass) -{ - ole_msg_loop(); - return Qnil; -} - -static void -add_event_call_back(VALUE obj, VALUE event, VALUE data) -{ - VALUE events = rb_ivar_get(obj, id_events); - if (NIL_P(events) || !RB_TYPE_P(events, T_ARRAY)) { - events = rb_ary_new(); - rb_ivar_set(obj, id_events, events); - } - ole_delete_event(events, event); - rb_ary_push(events, data); -} - -static VALUE -ev_on_event(int argc, VALUE *argv, VALUE self, VALUE is_ary_arg) -{ - struct oleeventdata *poleev; - VALUE event, args, data; - TypedData_Get_Struct(self, struct oleeventdata, &oleevent_datatype, poleev); - if (poleev->pConnectionPoint == NULL) { - rb_raise(eWIN32OLERuntimeError, "IConnectionPoint not found. You must call advise at first."); - } - rb_scan_args(argc, argv, "01*", &event, &args); - if(!NIL_P(event)) { - if(!RB_TYPE_P(event, T_STRING) && !RB_TYPE_P(event, T_SYMBOL)) { - rb_raise(rb_eTypeError, "wrong argument type (expected String or Symbol)"); - } - if (RB_TYPE_P(event, T_SYMBOL)) { - event = rb_sym2str(event); - } - } - data = rb_ary_new3(4, rb_block_proc(), event, args, is_ary_arg); - add_event_call_back(self, event, data); - return Qnil; -} - -/* - * call-seq: - * WIN32OLE_EVENT#on_event([event]){...} - * - * Defines the callback event. - * If argument is omitted, this method defines the callback of all events. - * If you want to modify reference argument in callback, return hash in - * callback. If you want to return value to OLE server as result of callback - * use `return' or :return. - * - * ie = WIN32OLE.new('InternetExplorer.Application') - * ev = WIN32OLE_EVENT.new(ie) - * ev.on_event("NavigateComplete") {|url| puts url} - * ev.on_event() {|ev, *args| puts "#{ev} fired"} - * - * ev.on_event("BeforeNavigate2") {|*args| - * ... - * # set true to BeforeNavigate reference argument `Cancel'. - * # Cancel is 7-th argument of BeforeNavigate, - * # so you can use 6 as key of hash instead of 'Cancel'. - * # The argument is counted from 0. - * # The hash key of 0 means first argument.) - * {:Cancel => true} # or {'Cancel' => true} or {6 => true} - * } - * - * ev.on_event(...) {|*args| - * {:return => 1, :xxx => yyy} - * } - */ -static VALUE -fev_on_event(int argc, VALUE *argv, VALUE self) -{ - return ev_on_event(argc, argv, self, Qfalse); -} - -/* - * call-seq: - * WIN32OLE_EVENT#on_event_with_outargs([event]){...} - * - * Defines the callback of event. - * If you want modify argument in callback, - * you could use this method instead of WIN32OLE_EVENT#on_event. - * - * ie = WIN32OLE.new('InternetExplorer.Application') - * ev = WIN32OLE_EVENT.new(ie) - * ev.on_event_with_outargs('BeforeNavigate2') {|*args| - * args.last[6] = true - * } - */ -static VALUE -fev_on_event_with_outargs(int argc, VALUE *argv, VALUE self) -{ - return ev_on_event(argc, argv, self, Qtrue); -} - -/* - * call-seq: - * WIN32OLE_EVENT#off_event([event]) - * - * removes the callback of event. - * - * ie = WIN32OLE.new('InternetExplorer.Application') - * ev = WIN32OLE_EVENT.new(ie) - * ev.on_event('BeforeNavigate2') {|*args| - * args.last[6] = true - * } - * ... - * ev.off_event('BeforeNavigate2') - * ... - */ -static VALUE -fev_off_event(int argc, VALUE *argv, VALUE self) -{ - VALUE event = Qnil; - VALUE events; - - rb_scan_args(argc, argv, "01", &event); - if(!NIL_P(event)) { - if(!RB_TYPE_P(event, T_STRING) && !RB_TYPE_P(event, T_SYMBOL)) { - rb_raise(rb_eTypeError, "wrong argument type (expected String or Symbol)"); - } - if (RB_TYPE_P(event, T_SYMBOL)) { - event = rb_sym2str(event); - } - } - events = rb_ivar_get(self, id_events); - if (NIL_P(events)) { - return Qnil; - } - ole_delete_event(events, event); - return Qnil; -} - -/* - * call-seq: - * WIN32OLE_EVENT#unadvise -> nil - * - * disconnects OLE server. If this method called, then the WIN32OLE_EVENT object - * does not receive the OLE server event any more. - * This method is trial implementation. - * - * ie = WIN32OLE.new('InternetExplorer.Application') - * ev = WIN32OLE_EVENT.new(ie) - * ev.on_event() {...} - * ... - * ev.unadvise - * - */ -static VALUE -fev_unadvise(VALUE self) -{ - struct oleeventdata *poleev; - TypedData_Get_Struct(self, struct oleeventdata, &oleevent_datatype, poleev); - if (poleev->pConnectionPoint) { - ole_msg_loop(); - evs_delete(poleev->event_id); - poleev->pConnectionPoint->lpVtbl->Unadvise(poleev->pConnectionPoint, poleev->dwCookie); - OLE_RELEASE(poleev->pConnectionPoint); - poleev->pConnectionPoint = NULL; - } - OLE_FREE(poleev->pDispatch); - return Qnil; -} - -static VALUE -evs_push(VALUE ev) -{ - return rb_ary_push(ary_ole_event, ev); -} - -static VALUE -evs_delete(long i) -{ - rb_ary_store(ary_ole_event, i, Qnil); - return Qnil; -} - -static VALUE -evs_entry(long i) -{ - return rb_ary_entry(ary_ole_event, i); -} - -static long -evs_length(void) -{ - return RARRAY_LEN(ary_ole_event); -} - -/* - * call-seq: - * WIN32OLE_EVENT#handler= - * - * sets event handler object. If handler object has onXXX - * method according to XXX event, then onXXX method is called - * when XXX event occurs. - * - * If handler object has method_missing and there is no - * method according to the event, then method_missing - * called and 1-st argument is event name. - * - * If handler object has onXXX method and there is block - * defined by WIN32OLE_EVENT#on_event('XXX'){}, - * then block is executed but handler object method is not called - * when XXX event occurs. - * - * class Handler - * def onStatusTextChange(text) - * puts "StatusTextChanged" - * end - * def onPropertyChange(prop) - * puts "PropertyChanged" - * end - * def method_missing(ev, *arg) - * puts "other event #{ev}" - * end - * end - * - * handler = Handler.new - * ie = WIN32OLE.new('InternetExplorer.Application') - * ev = WIN32OLE_EVENT.new(ie) - * ev.on_event("StatusTextChange") {|*args| - * puts "this block executed." - * puts "handler.onStatusTextChange method is not called." - * } - * ev.handler = handler - * - */ -static VALUE -fev_set_handler(VALUE self, VALUE val) -{ - return rb_ivar_set(self, rb_intern("handler"), val); -} - -/* - * call-seq: - * WIN32OLE_EVENT#handler - * - * returns handler object. - * - */ -static VALUE -fev_get_handler(VALUE self) -{ - return rb_ivar_get(self, rb_intern("handler")); -} - -void -Init_win32ole_event(void) -{ -#undef rb_intern - ary_ole_event = rb_ary_new(); - rb_gc_register_mark_object(ary_ole_event); - id_events = rb_intern("events"); - cWIN32OLE_EVENT = rb_define_class_under(cWIN32OLE, "Event", rb_cObject); - rb_define_const(rb_cObject, "WIN32OLE_EVENT", cWIN32OLE_EVENT); - rb_define_singleton_method(cWIN32OLE_EVENT, "message_loop", fev_s_msg_loop, 0); - rb_define_alloc_func(cWIN32OLE_EVENT, fev_s_allocate); - rb_define_method(cWIN32OLE_EVENT, "initialize", fev_initialize, -1); - rb_define_method(cWIN32OLE_EVENT, "on_event", fev_on_event, -1); - rb_define_method(cWIN32OLE_EVENT, "on_event_with_outargs", fev_on_event_with_outargs, -1); - rb_define_method(cWIN32OLE_EVENT, "off_event", fev_off_event, -1); - rb_define_method(cWIN32OLE_EVENT, "unadvise", fev_unadvise, 0); - rb_define_method(cWIN32OLE_EVENT, "handler=", fev_set_handler, 1); - rb_define_method(cWIN32OLE_EVENT, "handler", fev_get_handler, 0); -} diff --git a/ext/win32ole/win32ole_event.h b/ext/win32ole/win32ole_event.h deleted file mode 100644 index f1a5aa234d..0000000000 --- a/ext/win32ole/win32ole_event.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef WIN32OLE_EVENT_H -#define WIN32OLE_EVENT_H 1 - -void Init_win32ole_event(void); - -#endif diff --git a/ext/win32ole/win32ole_method.c b/ext/win32ole/win32ole_method.c deleted file mode 100644 index 646fdaf60c..0000000000 --- a/ext/win32ole/win32ole_method.c +++ /dev/null @@ -1,953 +0,0 @@ -#include "win32ole.h" - -static void olemethod_free(void *ptr); -static size_t olemethod_size(const void *ptr); -static VALUE ole_method_sub(VALUE self, ITypeInfo *pOwnerTypeInfo, ITypeInfo *pTypeInfo, VALUE name); -static VALUE olemethod_from_typeinfo(VALUE self, ITypeInfo *pTypeInfo, VALUE name); -static VALUE ole_methods_sub(ITypeInfo *pOwnerTypeInfo, ITypeInfo *pTypeInfo, VALUE methods, int mask); -static VALUE olemethod_set_member(VALUE self, ITypeInfo *pTypeInfo, ITypeInfo *pOwnerTypeInfo, int index, VALUE name); -static VALUE folemethod_initialize(VALUE self, VALUE oletype, VALUE method); -static VALUE folemethod_name(VALUE self); -static VALUE ole_method_return_type(ITypeInfo *pTypeInfo, UINT method_index); -static VALUE folemethod_return_type(VALUE self); -static VALUE ole_method_return_vtype(ITypeInfo *pTypeInfo, UINT method_index); -static VALUE folemethod_return_vtype(VALUE self); -static VALUE ole_method_return_type_detail(ITypeInfo *pTypeInfo, UINT method_index); -static VALUE folemethod_return_type_detail(VALUE self); -static VALUE ole_method_invkind(ITypeInfo *pTypeInfo, UINT method_index); -static VALUE ole_method_invoke_kind(ITypeInfo *pTypeInfo, UINT method_index); -static VALUE folemethod_invkind(VALUE self); -static VALUE folemethod_invoke_kind(VALUE self); -static VALUE ole_method_visible(ITypeInfo *pTypeInfo, UINT method_index); -static VALUE folemethod_visible(VALUE self); -static VALUE ole_method_event(ITypeInfo *pTypeInfo, UINT method_index, VALUE method_name); -static VALUE folemethod_event(VALUE self); -static VALUE folemethod_event_interface(VALUE self); -static HRESULT ole_method_docinfo_from_type(ITypeInfo *pTypeInfo, UINT method_index, BSTR *name, BSTR *helpstr, DWORD *helpcontext, BSTR *helpfile); -static VALUE ole_method_helpstring(ITypeInfo *pTypeInfo, UINT method_index); -static VALUE folemethod_helpstring(VALUE self); -static VALUE ole_method_helpfile(ITypeInfo *pTypeInfo, UINT method_index); -static VALUE folemethod_helpfile(VALUE self); -static VALUE ole_method_helpcontext(ITypeInfo *pTypeInfo, UINT method_index); -static VALUE folemethod_helpcontext(VALUE self); -static VALUE ole_method_dispid(ITypeInfo *pTypeInfo, UINT method_index); -static VALUE folemethod_dispid(VALUE self); -static VALUE ole_method_offset_vtbl(ITypeInfo *pTypeInfo, UINT method_index); -static VALUE folemethod_offset_vtbl(VALUE self); -static VALUE ole_method_size_params(ITypeInfo *pTypeInfo, UINT method_index); -static VALUE folemethod_size_params(VALUE self); -static VALUE ole_method_size_opt_params(ITypeInfo *pTypeInfo, UINT method_index); -static VALUE folemethod_size_opt_params(VALUE self); -static VALUE ole_method_params(ITypeInfo *pTypeInfo, UINT method_index); -static VALUE folemethod_params(VALUE self); -static VALUE folemethod_inspect(VALUE self); - -static const rb_data_type_t olemethod_datatype = { - "win32ole_method", - {NULL, olemethod_free, olemethod_size,}, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY -}; - -static void -olemethod_free(void *ptr) -{ - struct olemethoddata *polemethod = ptr; - OLE_FREE(polemethod->pTypeInfo); - OLE_FREE(polemethod->pOwnerTypeInfo); - free(polemethod); -} - -static size_t -olemethod_size(const void *ptr) -{ - return ptr ? sizeof(struct olemethoddata) : 0; -} - -struct olemethoddata * -olemethod_data_get_struct(VALUE obj) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(obj, struct olemethoddata, &olemethod_datatype, pmethod); - return pmethod; -} - -static VALUE -ole_method_sub(VALUE self, ITypeInfo *pOwnerTypeInfo, ITypeInfo *pTypeInfo, VALUE name) -{ - HRESULT hr; - TYPEATTR *pTypeAttr; - BSTR bstr; - FUNCDESC *pFuncDesc; - WORD i; - VALUE fname; - VALUE method = Qnil; - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeAttr"); - } - for(i = 0; i < pTypeAttr->cFuncs && method == Qnil; i++) { - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, i, &pFuncDesc); - if (FAILED(hr)) - continue; - - hr = pTypeInfo->lpVtbl->GetDocumentation(pTypeInfo, pFuncDesc->memid, - &bstr, NULL, NULL, NULL); - if (FAILED(hr)) { - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - continue; - } - fname = WC2VSTR(bstr); - if (strcasecmp(StringValuePtr(name), StringValuePtr(fname)) == 0) { - olemethod_set_member(self, pTypeInfo, pOwnerTypeInfo, i, fname); - method = self; - } - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - pFuncDesc=NULL; - } - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - return method; -} - -VALUE -ole_methods_from_typeinfo(ITypeInfo *pTypeInfo, int mask) -{ - HRESULT hr; - TYPEATTR *pTypeAttr; - WORD i; - HREFTYPE href; - ITypeInfo *pRefTypeInfo; - VALUE methods = rb_ary_new(); - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeAttr"); - } - - ole_methods_sub(0, pTypeInfo, methods, mask); - for(i=0; i < pTypeAttr->cImplTypes; i++){ - hr = pTypeInfo->lpVtbl->GetRefTypeOfImplType(pTypeInfo, i, &href); - if(FAILED(hr)) - continue; - hr = pTypeInfo->lpVtbl->GetRefTypeInfo(pTypeInfo, href, &pRefTypeInfo); - if (FAILED(hr)) - continue; - ole_methods_sub(pTypeInfo, pRefTypeInfo, methods, mask); - OLE_RELEASE(pRefTypeInfo); - } - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - return methods; -} - -static VALUE -olemethod_from_typeinfo(VALUE self, ITypeInfo *pTypeInfo, VALUE name) -{ - HRESULT hr; - TYPEATTR *pTypeAttr; - WORD i; - HREFTYPE href; - ITypeInfo *pRefTypeInfo; - VALUE method = Qnil; - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeAttr"); - } - method = ole_method_sub(self, 0, pTypeInfo, name); - if (method != Qnil) { - return method; - } - for(i=0; i < pTypeAttr->cImplTypes && method == Qnil; i++){ - hr = pTypeInfo->lpVtbl->GetRefTypeOfImplType(pTypeInfo, i, &href); - if(FAILED(hr)) - continue; - hr = pTypeInfo->lpVtbl->GetRefTypeInfo(pTypeInfo, href, &pRefTypeInfo); - if (FAILED(hr)) - continue; - method = ole_method_sub(self, pTypeInfo, pRefTypeInfo, name); - OLE_RELEASE(pRefTypeInfo); - } - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - return method; -} - -static VALUE -ole_methods_sub(ITypeInfo *pOwnerTypeInfo, ITypeInfo *pTypeInfo, VALUE methods, int mask) -{ - HRESULT hr; - TYPEATTR *pTypeAttr; - BSTR bstr; - FUNCDESC *pFuncDesc; - VALUE method; - WORD i; - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeAttr"); - } - for(i = 0; i < pTypeAttr->cFuncs; i++) { - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, i, &pFuncDesc); - if (FAILED(hr)) - continue; - - hr = pTypeInfo->lpVtbl->GetDocumentation(pTypeInfo, pFuncDesc->memid, - &bstr, NULL, NULL, NULL); - if (FAILED(hr)) { - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - continue; - } - if(pFuncDesc->invkind & mask) { - method = folemethod_s_allocate(cWIN32OLE_METHOD); - olemethod_set_member(method, pTypeInfo, pOwnerTypeInfo, - i, WC2VSTR(bstr)); - rb_ary_push(methods, method); - } - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - pFuncDesc=NULL; - } - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - - return methods; -} - -VALUE -create_win32ole_method(ITypeInfo *pTypeInfo, VALUE name) -{ - - VALUE method = folemethod_s_allocate(cWIN32OLE_METHOD); - VALUE obj = olemethod_from_typeinfo(method, pTypeInfo, name); - return obj; -} - -/* - * Document-class: WIN32OLE_METHOD - * - * <code>WIN32OLE_METHOD</code> objects represent OLE method information. - */ - -static VALUE -olemethod_set_member(VALUE self, ITypeInfo *pTypeInfo, ITypeInfo *pOwnerTypeInfo, int index, VALUE name) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - pmethod->pTypeInfo = pTypeInfo; - OLE_ADDREF(pTypeInfo); - pmethod->pOwnerTypeInfo = pOwnerTypeInfo; - OLE_ADDREF(pOwnerTypeInfo); - pmethod->index = index; - rb_ivar_set(self, rb_intern("name"), name); - return self; -} - -VALUE -folemethod_s_allocate(VALUE klass) -{ - struct olemethoddata *pmethod; - VALUE obj; - obj = TypedData_Make_Struct(klass, - struct olemethoddata, - &olemethod_datatype, pmethod); - pmethod->pTypeInfo = NULL; - pmethod->pOwnerTypeInfo = NULL; - pmethod->index = 0; - return obj; -} - -/* - * call-seq: - * WIN32OLE_METHOD.new(ole_type, method) -> WIN32OLE_METHOD object - * - * Returns a new WIN32OLE_METHOD object which represents the information - * about OLE method. - * The first argument <i>ole_type</i> specifies WIN32OLE_TYPE object. - * The second argument <i>method</i> specifies OLE method name defined OLE class - * which represents WIN32OLE_TYPE object. - * - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook') - * method = WIN32OLE_METHOD.new(tobj, 'SaveAs') - */ -static VALUE -folemethod_initialize(VALUE self, VALUE oletype, VALUE method) -{ - VALUE obj = Qnil; - ITypeInfo *pTypeInfo; - if (rb_obj_is_kind_of(oletype, cWIN32OLE_TYPE)) { - SafeStringValue(method); - pTypeInfo = itypeinfo(oletype); - obj = olemethod_from_typeinfo(self, pTypeInfo, method); - if (obj == Qnil) { - rb_raise(eWIN32OLERuntimeError, "not found %s", - StringValuePtr(method)); - } - } - else { - rb_raise(rb_eTypeError, "1st argument should be WIN32OLE_TYPE object"); - } - return obj; -} - -/* - * call-seq: - * WIN32OLE_METHOD#name - * - * Returns the name of the method. - * - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook') - * method = WIN32OLE_METHOD.new(tobj, 'SaveAs') - * puts method.name # => SaveAs - * - */ -static VALUE -folemethod_name(VALUE self) -{ - return rb_ivar_get(self, rb_intern("name")); -} - -static VALUE -ole_method_return_type(ITypeInfo *pTypeInfo, UINT method_index) -{ - FUNCDESC *pFuncDesc; - HRESULT hr; - VALUE type; - - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc); - if (FAILED(hr)) - ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetFuncDesc"); - - type = ole_typedesc2val(pTypeInfo, &(pFuncDesc->elemdescFunc.tdesc), Qnil); - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return type; -} - -/* - * call-seq: - * WIN32OLE_METHOD#return_type - * - * Returns string of return value type of method. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks') - * method = WIN32OLE_METHOD.new(tobj, 'Add') - * puts method.return_type # => Workbook - * - */ -static VALUE -folemethod_return_type(VALUE self) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - return ole_method_return_type(pmethod->pTypeInfo, pmethod->index); -} - -static VALUE -ole_method_return_vtype(ITypeInfo *pTypeInfo, UINT method_index) -{ - FUNCDESC *pFuncDesc; - HRESULT hr; - VALUE vvt; - - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc); - if (FAILED(hr)) - ole_raise(hr, eWIN32OLERuntimeError, "failed to GetFuncDesc"); - - vvt = RB_INT2FIX(pFuncDesc->elemdescFunc.tdesc.vt); - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return vvt; -} - -/* - * call-seq: - * WIN32OLE_METHOD#return_vtype - * - * Returns number of return value type of method. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks') - * method = WIN32OLE_METHOD.new(tobj, 'Add') - * puts method.return_vtype # => 26 - * - */ -static VALUE -folemethod_return_vtype(VALUE self) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - return ole_method_return_vtype(pmethod->pTypeInfo, pmethod->index); -} - -static VALUE -ole_method_return_type_detail(ITypeInfo *pTypeInfo, UINT method_index) -{ - FUNCDESC *pFuncDesc; - HRESULT hr; - VALUE type = rb_ary_new(); - - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc); - if (FAILED(hr)) - return type; - - ole_typedesc2val(pTypeInfo, &(pFuncDesc->elemdescFunc.tdesc), type); - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return type; -} - -/* - * call-seq: - * WIN32OLE_METHOD#return_type_detail - * - * Returns detail information of return value type of method. - * The information is array. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks') - * method = WIN32OLE_METHOD.new(tobj, 'Add') - * p method.return_type_detail # => ["PTR", "USERDEFINED", "Workbook"] - */ -static VALUE -folemethod_return_type_detail(VALUE self) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - return ole_method_return_type_detail(pmethod->pTypeInfo, pmethod->index); -} - -static VALUE -ole_method_invkind(ITypeInfo *pTypeInfo, UINT method_index) -{ - FUNCDESC *pFuncDesc; - HRESULT hr; - VALUE invkind; - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc); - if(FAILED(hr)) - ole_raise(hr, eWIN32OLERuntimeError, "failed to GetFuncDesc"); - invkind = RB_INT2FIX(pFuncDesc->invkind); - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return invkind; -} - -static VALUE -ole_method_invoke_kind(ITypeInfo *pTypeInfo, UINT method_index) -{ - VALUE type = rb_str_new2("UNKNOWN"); - VALUE invkind = ole_method_invkind(pTypeInfo, method_index); - if((RB_FIX2INT(invkind) & INVOKE_PROPERTYGET) && - (RB_FIX2INT(invkind) & INVOKE_PROPERTYPUT) ) { - type = rb_str_new2("PROPERTY"); - } else if(RB_FIX2INT(invkind) & INVOKE_PROPERTYGET) { - type = rb_str_new2("PROPERTYGET"); - } else if(RB_FIX2INT(invkind) & INVOKE_PROPERTYPUT) { - type = rb_str_new2("PROPERTYPUT"); - } else if(RB_FIX2INT(invkind) & INVOKE_PROPERTYPUTREF) { - type = rb_str_new2("PROPERTYPUTREF"); - } else if(RB_FIX2INT(invkind) & INVOKE_FUNC) { - type = rb_str_new2("FUNC"); - } - return type; -} - -/* - * call-seq: - * WIN32OLE_METHOD#invkind - * - * Returns the method invoke kind. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks') - * method = WIN32OLE_METHOD.new(tobj, 'Add') - * puts method.invkind # => 1 - * - */ -static VALUE -folemethod_invkind(VALUE self) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - return ole_method_invkind(pmethod->pTypeInfo, pmethod->index); -} - -/* - * call-seq: - * WIN32OLE_METHOD#invoke_kind - * - * Returns the method kind string. The string is "UNKNOWN" or "PROPERTY" - * or "PROPERTY" or "PROPERTYGET" or "PROPERTYPUT" or "PROPERTYPPUTREF" - * or "FUNC". - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks') - * method = WIN32OLE_METHOD.new(tobj, 'Add') - * puts method.invoke_kind # => "FUNC" - */ -static VALUE -folemethod_invoke_kind(VALUE self) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - return ole_method_invoke_kind(pmethod->pTypeInfo, pmethod->index); -} - -static VALUE -ole_method_visible(ITypeInfo *pTypeInfo, UINT method_index) -{ - FUNCDESC *pFuncDesc; - HRESULT hr; - VALUE visible; - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc); - if(FAILED(hr)) - return Qfalse; - if (pFuncDesc->wFuncFlags & (FUNCFLAG_FRESTRICTED | - FUNCFLAG_FHIDDEN | - FUNCFLAG_FNONBROWSABLE)) { - visible = Qfalse; - } else { - visible = Qtrue; - } - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return visible; -} - -/* - * call-seq: - * WIN32OLE_METHOD#visible? - * - * Returns true if the method is public. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks') - * method = WIN32OLE_METHOD.new(tobj, 'Add') - * puts method.visible? # => true - */ -static VALUE -folemethod_visible(VALUE self) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - return ole_method_visible(pmethod->pTypeInfo, pmethod->index); -} - -static VALUE -ole_method_event(ITypeInfo *pTypeInfo, UINT method_index, VALUE method_name) -{ - TYPEATTR *pTypeAttr; - HRESULT hr; - WORD i; - int flags; - HREFTYPE href; - ITypeInfo *pRefTypeInfo; - FUNCDESC *pFuncDesc; - BSTR bstr; - VALUE name; - VALUE event = Qfalse; - - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if (FAILED(hr)) - return event; - if(pTypeAttr->typekind != TKIND_COCLASS) { - pTypeInfo->lpVtbl->ReleaseTypeAttr(pTypeInfo, pTypeAttr); - return event; - } - for (i = 0; i < pTypeAttr->cImplTypes; i++) { - hr = pTypeInfo->lpVtbl->GetImplTypeFlags(pTypeInfo, i, &flags); - if (FAILED(hr)) - continue; - - if (flags & IMPLTYPEFLAG_FSOURCE) { - hr = pTypeInfo->lpVtbl->GetRefTypeOfImplType(pTypeInfo, - i, &href); - if (FAILED(hr)) - continue; - hr = pTypeInfo->lpVtbl->GetRefTypeInfo(pTypeInfo, - href, &pRefTypeInfo); - if (FAILED(hr)) - continue; - hr = pRefTypeInfo->lpVtbl->GetFuncDesc(pRefTypeInfo, method_index, - &pFuncDesc); - if (FAILED(hr)) { - OLE_RELEASE(pRefTypeInfo); - continue; - } - - hr = pRefTypeInfo->lpVtbl->GetDocumentation(pRefTypeInfo, - pFuncDesc->memid, - &bstr, NULL, NULL, NULL); - if (FAILED(hr)) { - pRefTypeInfo->lpVtbl->ReleaseFuncDesc(pRefTypeInfo, pFuncDesc); - OLE_RELEASE(pRefTypeInfo); - continue; - } - - name = WC2VSTR(bstr); - pRefTypeInfo->lpVtbl->ReleaseFuncDesc(pRefTypeInfo, pFuncDesc); - OLE_RELEASE(pRefTypeInfo); - if (rb_str_cmp(method_name, name) == 0) { - event = Qtrue; - break; - } - } - } - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - return event; -} - -/* - * call-seq: - * WIN32OLE_METHOD#event? - * - * Returns true if the method is event. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook') - * method = WIN32OLE_METHOD.new(tobj, 'SheetActivate') - * puts method.event? # => true - * - */ -static VALUE -folemethod_event(VALUE self) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - if (!pmethod->pOwnerTypeInfo) - return Qfalse; - return ole_method_event(pmethod->pOwnerTypeInfo, - pmethod->index, - rb_ivar_get(self, rb_intern("name"))); -} - -/* - * call-seq: - * WIN32OLE_METHOD#event_interface - * - * Returns event interface name if the method is event. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook') - * method = WIN32OLE_METHOD.new(tobj, 'SheetActivate') - * puts method.event_interface # => WorkbookEvents - */ -static VALUE -folemethod_event_interface(VALUE self) -{ - BSTR name; - struct olemethoddata *pmethod; - HRESULT hr; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - if(folemethod_event(self) == Qtrue) { - hr = ole_docinfo_from_type(pmethod->pTypeInfo, &name, NULL, NULL, NULL); - if(SUCCEEDED(hr)) - return WC2VSTR(name); - } - return Qnil; -} - -static HRESULT -ole_method_docinfo_from_type( - ITypeInfo *pTypeInfo, - UINT method_index, - BSTR *name, - BSTR *helpstr, - DWORD *helpcontext, - BSTR *helpfile - ) -{ - FUNCDESC *pFuncDesc; - HRESULT hr; - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc); - if (FAILED(hr)) - return hr; - hr = pTypeInfo->lpVtbl->GetDocumentation(pTypeInfo, pFuncDesc->memid, - name, helpstr, - helpcontext, helpfile); - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return hr; -} - -static VALUE -ole_method_helpstring(ITypeInfo *pTypeInfo, UINT method_index) -{ - HRESULT hr; - BSTR bhelpstring; - hr = ole_method_docinfo_from_type(pTypeInfo, method_index, NULL, &bhelpstring, - NULL, NULL); - if (FAILED(hr)) - return Qnil; - return WC2VSTR(bhelpstring); -} - -/* - * call-seq: - * WIN32OLE_METHOD#helpstring - * - * Returns help string of OLE method. If the help string is not found, - * then the method returns nil. - * tobj = WIN32OLE_TYPE.new('Microsoft Internet Controls', 'IWebBrowser') - * method = WIN32OLE_METHOD.new(tobj, 'Navigate') - * puts method.helpstring # => Navigates to a URL or file. - * - */ -static VALUE -folemethod_helpstring(VALUE self) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - return ole_method_helpstring(pmethod->pTypeInfo, pmethod->index); -} - -static VALUE -ole_method_helpfile(ITypeInfo *pTypeInfo, UINT method_index) -{ - HRESULT hr; - BSTR bhelpfile; - hr = ole_method_docinfo_from_type(pTypeInfo, method_index, NULL, NULL, - NULL, &bhelpfile); - if (FAILED(hr)) - return Qnil; - return WC2VSTR(bhelpfile); -} - -/* - * call-seq: - * WIN32OLE_METHOD#helpfile - * - * Returns help file. If help file is not found, then - * the method returns nil. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks') - * method = WIN32OLE_METHOD.new(tobj, 'Add') - * puts method.helpfile # => C:\...\VBAXL9.CHM - */ -static VALUE -folemethod_helpfile(VALUE self) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - - return ole_method_helpfile(pmethod->pTypeInfo, pmethod->index); -} - -static VALUE -ole_method_helpcontext(ITypeInfo *pTypeInfo, UINT method_index) -{ - HRESULT hr; - DWORD helpcontext = 0; - hr = ole_method_docinfo_from_type(pTypeInfo, method_index, NULL, NULL, - &helpcontext, NULL); - if (FAILED(hr)) - return Qnil; - return RB_INT2FIX(helpcontext); -} - -/* - * call-seq: - * WIN32OLE_METHOD#helpcontext - * - * Returns help context. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks') - * method = WIN32OLE_METHOD.new(tobj, 'Add') - * puts method.helpcontext # => 65717 - */ -static VALUE -folemethod_helpcontext(VALUE self) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - return ole_method_helpcontext(pmethod->pTypeInfo, pmethod->index); -} - -static VALUE -ole_method_dispid(ITypeInfo *pTypeInfo, UINT method_index) -{ - FUNCDESC *pFuncDesc; - HRESULT hr; - VALUE dispid = Qnil; - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc); - if (FAILED(hr)) - return dispid; - dispid = RB_INT2NUM(pFuncDesc->memid); - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return dispid; -} - -/* - * call-seq: - * WIN32OLE_METHOD#dispid - * - * Returns dispatch ID. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks') - * method = WIN32OLE_METHOD.new(tobj, 'Add') - * puts method.dispid # => 181 - */ -static VALUE -folemethod_dispid(VALUE self) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - return ole_method_dispid(pmethod->pTypeInfo, pmethod->index); -} - -static VALUE -ole_method_offset_vtbl(ITypeInfo *pTypeInfo, UINT method_index) -{ - FUNCDESC *pFuncDesc; - HRESULT hr; - VALUE offset_vtbl = Qnil; - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc); - if (FAILED(hr)) - return offset_vtbl; - offset_vtbl = RB_INT2FIX(pFuncDesc->oVft); - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return offset_vtbl; -} - -/* - * call-seq: - * WIN32OLE_METHOD#offset_vtbl - * - * Returns the offset ov VTBL. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks') - * method = WIN32OLE_METHOD.new(tobj, 'Add') - * puts method.offset_vtbl # => 40 - */ -static VALUE -folemethod_offset_vtbl(VALUE self) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - return ole_method_offset_vtbl(pmethod->pTypeInfo, pmethod->index); -} - -static VALUE -ole_method_size_params(ITypeInfo *pTypeInfo, UINT method_index) -{ - FUNCDESC *pFuncDesc; - HRESULT hr; - VALUE size_params = Qnil; - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc); - if (FAILED(hr)) - return size_params; - size_params = RB_INT2FIX(pFuncDesc->cParams); - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return size_params; -} - -/* - * call-seq: - * WIN32OLE_METHOD#size_params - * - * Returns the size of arguments of the method. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook') - * method = WIN32OLE_METHOD.new(tobj, 'SaveAs') - * puts method.size_params # => 11 - * - */ -static VALUE -folemethod_size_params(VALUE self) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - return ole_method_size_params(pmethod->pTypeInfo, pmethod->index); -} - -static VALUE -ole_method_size_opt_params(ITypeInfo *pTypeInfo, UINT method_index) -{ - FUNCDESC *pFuncDesc; - HRESULT hr; - VALUE size_opt_params = Qnil; - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc); - if (FAILED(hr)) - return size_opt_params; - size_opt_params = RB_INT2FIX(pFuncDesc->cParamsOpt); - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return size_opt_params; -} - -/* - * call-seq: - * WIN32OLE_METHOD#size_opt_params - * - * Returns the size of optional parameters. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook') - * method = WIN32OLE_METHOD.new(tobj, 'SaveAs') - * puts method.size_opt_params # => 4 - */ -static VALUE -folemethod_size_opt_params(VALUE self) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - return ole_method_size_opt_params(pmethod->pTypeInfo, pmethod->index); -} - -static VALUE -ole_method_params(ITypeInfo *pTypeInfo, UINT method_index) -{ - FUNCDESC *pFuncDesc; - HRESULT hr; - BSTR *bstrs; - UINT len, i; - VALUE param; - VALUE params = rb_ary_new(); - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc); - if (FAILED(hr)) - return params; - - len = 0; - bstrs = ALLOCA_N(BSTR, pFuncDesc->cParams + 1); - hr = pTypeInfo->lpVtbl->GetNames(pTypeInfo, pFuncDesc->memid, - bstrs, pFuncDesc->cParams + 1, - &len); - if (FAILED(hr)) { - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return params; - } - SysFreeString(bstrs[0]); - if (pFuncDesc->cParams > 0) { - for(i = 1; i < len; i++) { - param = create_win32ole_param(pTypeInfo, method_index, i-1, WC2VSTR(bstrs[i])); - rb_ary_push(params, param); - } - } - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return params; -} - -/* - * call-seq: - * WIN32OLE_METHOD#params - * - * returns array of WIN32OLE_PARAM object corresponding with method parameters. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook') - * method = WIN32OLE_METHOD.new(tobj, 'SaveAs') - * p method.params # => [Filename, FileFormat, Password, WriteResPassword, - * ReadOnlyRecommended, CreateBackup, AccessMode, - * ConflictResolution, AddToMru, TextCodepage, - * TextVisualLayout] - */ -static VALUE -folemethod_params(VALUE self) -{ - struct olemethoddata *pmethod; - TypedData_Get_Struct(self, struct olemethoddata, &olemethod_datatype, pmethod); - return ole_method_params(pmethod->pTypeInfo, pmethod->index); -} - -/* - * call-seq: - * WIN32OLE_METHOD#inspect -> String - * - * Returns the method name with class name. - * - */ -static VALUE -folemethod_inspect(VALUE self) -{ - return default_inspect(self, "WIN32OLE_METHOD"); -} - -VALUE cWIN32OLE_METHOD; - -void Init_win32ole_method(void) -{ - cWIN32OLE_METHOD = rb_define_class_under(cWIN32OLE, "Method", rb_cObject); - rb_define_const(rb_cObject, "WIN32OLE_METHOD", cWIN32OLE_METHOD); - rb_define_alloc_func(cWIN32OLE_METHOD, folemethod_s_allocate); - rb_define_method(cWIN32OLE_METHOD, "initialize", folemethod_initialize, 2); - rb_define_method(cWIN32OLE_METHOD, "name", folemethod_name, 0); - rb_define_method(cWIN32OLE_METHOD, "return_type", folemethod_return_type, 0); - rb_define_method(cWIN32OLE_METHOD, "return_vtype", folemethod_return_vtype, 0); - rb_define_method(cWIN32OLE_METHOD, "return_type_detail", folemethod_return_type_detail, 0); - rb_define_method(cWIN32OLE_METHOD, "invoke_kind", folemethod_invoke_kind, 0); - rb_define_method(cWIN32OLE_METHOD, "invkind", folemethod_invkind, 0); - rb_define_method(cWIN32OLE_METHOD, "visible?", folemethod_visible, 0); - rb_define_method(cWIN32OLE_METHOD, "event?", folemethod_event, 0); - rb_define_method(cWIN32OLE_METHOD, "event_interface", folemethod_event_interface, 0); - rb_define_method(cWIN32OLE_METHOD, "helpstring", folemethod_helpstring, 0); - rb_define_method(cWIN32OLE_METHOD, "helpfile", folemethod_helpfile, 0); - rb_define_method(cWIN32OLE_METHOD, "helpcontext", folemethod_helpcontext, 0); - rb_define_method(cWIN32OLE_METHOD, "dispid", folemethod_dispid, 0); - rb_define_method(cWIN32OLE_METHOD, "offset_vtbl", folemethod_offset_vtbl, 0); - rb_define_method(cWIN32OLE_METHOD, "size_params", folemethod_size_params, 0); - rb_define_method(cWIN32OLE_METHOD, "size_opt_params", folemethod_size_opt_params, 0); - rb_define_method(cWIN32OLE_METHOD, "params", folemethod_params, 0); - rb_define_alias(cWIN32OLE_METHOD, "to_s", "name"); - rb_define_method(cWIN32OLE_METHOD, "inspect", folemethod_inspect, 0); -} diff --git a/ext/win32ole/win32ole_method.h b/ext/win32ole/win32ole_method.h deleted file mode 100644 index ef907d2fac..0000000000 --- a/ext/win32ole/win32ole_method.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef WIN32OLE_METHOD_H -#define WIN32OLE_METHOD_H 1 - -struct olemethoddata { - ITypeInfo *pOwnerTypeInfo; - ITypeInfo *pTypeInfo; - UINT index; -}; - -extern VALUE cWIN32OLE_METHOD; -VALUE folemethod_s_allocate(VALUE klass); -VALUE ole_methods_from_typeinfo(ITypeInfo *pTypeInfo, int mask); -VALUE create_win32ole_method(ITypeInfo *pTypeInfo, VALUE name); -struct olemethoddata *olemethod_data_get_struct(VALUE obj); -void Init_win32ole_method(void); -#endif diff --git a/ext/win32ole/win32ole_param.c b/ext/win32ole/win32ole_param.c deleted file mode 100644 index c6f324968b..0000000000 --- a/ext/win32ole/win32ole_param.c +++ /dev/null @@ -1,439 +0,0 @@ -#include "win32ole.h" - -VALUE cWIN32OLE_PARAM; - -struct oleparamdata { - ITypeInfo *pTypeInfo; - UINT method_index; - UINT index; -}; - -static void oleparam_free(void *ptr); -static size_t oleparam_size(const void *ptr); -static VALUE foleparam_s_allocate(VALUE klass); -static VALUE oleparam_ole_param_from_index(VALUE self, ITypeInfo *pTypeInfo, UINT method_index, int param_index); -static VALUE oleparam_ole_param(VALUE self, VALUE olemethod, int n); -static VALUE foleparam_initialize(VALUE self, VALUE olemethod, VALUE n); -static VALUE foleparam_name(VALUE self); -static VALUE ole_param_ole_type(ITypeInfo *pTypeInfo, UINT method_index, UINT index); -static VALUE foleparam_ole_type(VALUE self); -static VALUE ole_param_ole_type_detail(ITypeInfo *pTypeInfo, UINT method_index, UINT index); -static VALUE foleparam_ole_type_detail(VALUE self); -static VALUE ole_param_flag_mask(ITypeInfo *pTypeInfo, UINT method_index, UINT index, USHORT mask); -static VALUE foleparam_input(VALUE self); -static VALUE foleparam_output(VALUE self); -static VALUE foleparam_optional(VALUE self); -static VALUE foleparam_retval(VALUE self); -static VALUE ole_param_default(ITypeInfo *pTypeInfo, UINT method_index, UINT index); -static VALUE foleparam_default(VALUE self); -static VALUE foleparam_inspect(VALUE self); - -static const rb_data_type_t oleparam_datatype = { - "win32ole_param", - {NULL, oleparam_free, oleparam_size,}, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY -}; - -static void -oleparam_free(void *ptr) -{ - struct oleparamdata *pole = ptr; - OLE_FREE(pole->pTypeInfo); - free(pole); -} - -static size_t -oleparam_size(const void *ptr) -{ - return ptr ? sizeof(struct oleparamdata) : 0; -} - -VALUE -create_win32ole_param(ITypeInfo *pTypeInfo, UINT method_index, UINT index, VALUE name) -{ - struct oleparamdata *pparam; - VALUE obj = foleparam_s_allocate(cWIN32OLE_PARAM); - TypedData_Get_Struct(obj, struct oleparamdata, &oleparam_datatype, pparam); - - pparam->pTypeInfo = pTypeInfo; - OLE_ADDREF(pTypeInfo); - pparam->method_index = method_index; - pparam->index = index; - rb_ivar_set(obj, rb_intern("name"), name); - return obj; -} - -/* - * Document-class: WIN32OLE_PARAM - * - * <code>WIN32OLE_PARAM</code> objects represent param information of - * the OLE method. - */ -static VALUE -foleparam_s_allocate(VALUE klass) -{ - struct oleparamdata *pparam; - VALUE obj; - obj = TypedData_Make_Struct(klass, - struct oleparamdata, - &oleparam_datatype, pparam); - pparam->pTypeInfo = NULL; - pparam->method_index = 0; - pparam->index = 0; - return obj; -} - -static VALUE -oleparam_ole_param_from_index(VALUE self, ITypeInfo *pTypeInfo, UINT method_index, int param_index) -{ - FUNCDESC *pFuncDesc; - HRESULT hr; - BSTR *bstrs; - UINT len; - struct oleparamdata *pparam; - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc); - if (FAILED(hr)) - ole_raise(hr, rb_eRuntimeError, "fail to ITypeInfo::GetFuncDesc"); - - len = 0; - bstrs = ALLOCA_N(BSTR, pFuncDesc->cParams + 1); - hr = pTypeInfo->lpVtbl->GetNames(pTypeInfo, pFuncDesc->memid, - bstrs, pFuncDesc->cParams + 1, - &len); - if (FAILED(hr)) { - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - ole_raise(hr, rb_eRuntimeError, "fail to ITypeInfo::GetNames"); - } - SysFreeString(bstrs[0]); - if (param_index < 1 || len <= (UINT)param_index) - { - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - rb_raise(rb_eIndexError, "index of param must be in 1..%d", len); - } - - TypedData_Get_Struct(self, struct oleparamdata, &oleparam_datatype, pparam); - pparam->pTypeInfo = pTypeInfo; - OLE_ADDREF(pTypeInfo); - pparam->method_index = method_index; - pparam->index = param_index - 1; - rb_ivar_set(self, rb_intern("name"), WC2VSTR(bstrs[param_index])); - - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return self; -} - -static VALUE -oleparam_ole_param(VALUE self, VALUE olemethod, int n) -{ - struct olemethoddata *pmethod = olemethod_data_get_struct(olemethod); - return oleparam_ole_param_from_index(self, pmethod->pTypeInfo, pmethod->index, n); -} - -/* - * call-seq: - * WIN32OLE_PARAM.new(method, n) -> WIN32OLE_PARAM object - * - * Returns WIN32OLE_PARAM object which represents OLE parameter information. - * 1st argument should be WIN32OLE_METHOD object. - * 2nd argument `n' is n-th parameter of the method specified by 1st argument. - * - * tobj = WIN32OLE_TYPE.new('Microsoft Scripting Runtime', 'IFileSystem') - * method = WIN32OLE_METHOD.new(tobj, 'CreateTextFile') - * param = WIN32OLE_PARAM.new(method, 2) # => #<WIN32OLE_PARAM:Overwrite=true> - * - */ -static VALUE -foleparam_initialize(VALUE self, VALUE olemethod, VALUE n) -{ - int idx; - if (!rb_obj_is_kind_of(olemethod, cWIN32OLE_METHOD)) { - rb_raise(rb_eTypeError, "1st parameter must be WIN32OLE_METHOD object"); - } - idx = RB_FIX2INT(n); - return oleparam_ole_param(self, olemethod, idx); -} - -/* - * call-seq: - * WIN32OLE_PARAM#name - * - * Returns name. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook') - * method = WIN32OLE_METHOD.new(tobj, 'SaveAs') - * param1 = method.params[0] - * puts param1.name # => Filename - */ -static VALUE -foleparam_name(VALUE self) -{ - return rb_ivar_get(self, rb_intern("name")); -} - -static VALUE -ole_param_ole_type(ITypeInfo *pTypeInfo, UINT method_index, UINT index) -{ - FUNCDESC *pFuncDesc; - HRESULT hr; - VALUE type = rb_str_new2("unknown type"); - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc); - if (FAILED(hr)) - return type; - type = ole_typedesc2val(pTypeInfo, - &(pFuncDesc->lprgelemdescParam[index].tdesc), Qnil); - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return type; -} - -/* - * call-seq: - * WIN32OLE_PARAM#ole_type - * - * Returns OLE type of WIN32OLE_PARAM object(parameter of OLE method). - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook') - * method = WIN32OLE_METHOD.new(tobj, 'SaveAs') - * param1 = method.params[0] - * puts param1.ole_type # => VARIANT - */ -static VALUE -foleparam_ole_type(VALUE self) -{ - struct oleparamdata *pparam; - TypedData_Get_Struct(self, struct oleparamdata, &oleparam_datatype, pparam); - return ole_param_ole_type(pparam->pTypeInfo, pparam->method_index, - pparam->index); -} - -static VALUE -ole_param_ole_type_detail(ITypeInfo *pTypeInfo, UINT method_index, UINT index) -{ - FUNCDESC *pFuncDesc; - HRESULT hr; - VALUE typedetail = rb_ary_new(); - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc); - if (FAILED(hr)) - return typedetail; - ole_typedesc2val(pTypeInfo, - &(pFuncDesc->lprgelemdescParam[index].tdesc), typedetail); - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return typedetail; -} - -/* - * call-seq: - * WIN32OLE_PARAM#ole_type_detail - * - * Returns detail information of type of argument. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'IWorksheetFunction') - * method = WIN32OLE_METHOD.new(tobj, 'SumIf') - * param1 = method.params[0] - * p param1.ole_type_detail # => ["PTR", "USERDEFINED", "Range"] - */ -static VALUE -foleparam_ole_type_detail(VALUE self) -{ - struct oleparamdata *pparam; - TypedData_Get_Struct(self, struct oleparamdata, &oleparam_datatype, pparam); - return ole_param_ole_type_detail(pparam->pTypeInfo, pparam->method_index, - pparam->index); -} - -static VALUE -ole_param_flag_mask(ITypeInfo *pTypeInfo, UINT method_index, UINT index, USHORT mask) -{ - FUNCDESC *pFuncDesc; - HRESULT hr; - VALUE ret = Qfalse; - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc); - if(FAILED(hr)) - return ret; - if (V_UNION1((&(pFuncDesc->lprgelemdescParam[index])), paramdesc).wParamFlags &mask) - ret = Qtrue; - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return ret; -} - -/* - * call-seq: - * WIN32OLE_PARAM#input? - * - * Returns true if the parameter is input. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook') - * method = WIN32OLE_METHOD.new(tobj, 'SaveAs') - * param1 = method.params[0] - * puts param1.input? # => true - */ -static VALUE -foleparam_input(VALUE self) -{ - struct oleparamdata *pparam; - TypedData_Get_Struct(self, struct oleparamdata, &oleparam_datatype, pparam); - return ole_param_flag_mask(pparam->pTypeInfo, pparam->method_index, - pparam->index, PARAMFLAG_FIN); -} - -/* - * call-seq: - * WIN32OLE#output? - * - * Returns true if argument is output. - * tobj = WIN32OLE_TYPE.new('Microsoft Internet Controls', 'DWebBrowserEvents') - * method = WIN32OLE_METHOD.new(tobj, 'NewWindow') - * method.params.each do |param| - * puts "#{param.name} #{param.output?}" - * end - * - * The result of above script is following: - * URL false - * Flags false - * TargetFrameName false - * PostData false - * Headers false - * Processed true - */ -static VALUE -foleparam_output(VALUE self) -{ - struct oleparamdata *pparam; - TypedData_Get_Struct(self, struct oleparamdata, &oleparam_datatype, pparam); - return ole_param_flag_mask(pparam->pTypeInfo, pparam->method_index, - pparam->index, PARAMFLAG_FOUT); -} - -/* - * call-seq: - * WIN32OLE_PARAM#optional? - * - * Returns true if argument is optional. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook') - * method = WIN32OLE_METHOD.new(tobj, 'SaveAs') - * param1 = method.params[0] - * puts "#{param1.name} #{param1.optional?}" # => Filename true - */ -static VALUE -foleparam_optional(VALUE self) -{ - struct oleparamdata *pparam; - TypedData_Get_Struct(self, struct oleparamdata, &oleparam_datatype, pparam); - return ole_param_flag_mask(pparam->pTypeInfo, pparam->method_index, - pparam->index, PARAMFLAG_FOPT); -} - -/* - * call-seq: - * WIN32OLE_PARAM#retval? - * - * Returns true if argument is return value. - * tobj = WIN32OLE_TYPE.new('DirectX 7 for Visual Basic Type Library', - * 'DirectPlayLobbyConnection') - * method = WIN32OLE_METHOD.new(tobj, 'GetPlayerShortName') - * param = method.params[0] - * puts "#{param.name} #{param.retval?}" # => name true - */ -static VALUE -foleparam_retval(VALUE self) -{ - struct oleparamdata *pparam; - TypedData_Get_Struct(self, struct oleparamdata, &oleparam_datatype, pparam); - return ole_param_flag_mask(pparam->pTypeInfo, pparam->method_index, - pparam->index, PARAMFLAG_FRETVAL); -} - -static VALUE -ole_param_default(ITypeInfo *pTypeInfo, UINT method_index, UINT index) -{ - FUNCDESC *pFuncDesc; - ELEMDESC *pElemDesc; - PARAMDESCEX * pParamDescEx; - HRESULT hr; - USHORT wParamFlags; - USHORT mask = PARAMFLAG_FOPT|PARAMFLAG_FHASDEFAULT; - VALUE defval = Qnil; - hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc); - if (FAILED(hr)) - return defval; - pElemDesc = &pFuncDesc->lprgelemdescParam[index]; - wParamFlags = V_UNION1(pElemDesc, paramdesc).wParamFlags; - if ((wParamFlags & mask) == mask) { - pParamDescEx = V_UNION1(pElemDesc, paramdesc).pparamdescex; - defval = ole_variant2val(&pParamDescEx->varDefaultValue); - } - pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc); - return defval; -} - -/* - * call-seq: - * WIN32OLE_PARAM#default - * - * Returns default value. If the default value does not exist, - * this method returns nil. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook') - * method = WIN32OLE_METHOD.new(tobj, 'SaveAs') - * method.params.each do |param| - * if param.default - * puts "#{param.name} (= #{param.default})" - * else - * puts "#{param}" - * end - * end - * - * The above script result is following: - * Filename - * FileFormat - * Password - * WriteResPassword - * ReadOnlyRecommended - * CreateBackup - * AccessMode (= 1) - * ConflictResolution - * AddToMru - * TextCodepage - * TextVisualLayout - */ -static VALUE -foleparam_default(VALUE self) -{ - struct oleparamdata *pparam; - TypedData_Get_Struct(self, struct oleparamdata, &oleparam_datatype, pparam); - return ole_param_default(pparam->pTypeInfo, pparam->method_index, - pparam->index); -} - -/* - * call-seq: - * WIN32OLE_PARAM#inspect -> String - * - * Returns the parameter name with class name. If the parameter has default value, - * then returns name=value string with class name. - * - */ -static VALUE -foleparam_inspect(VALUE self) -{ - VALUE detail = foleparam_name(self); - VALUE defval = foleparam_default(self); - if (defval != Qnil) { - rb_str_cat2(detail, "="); - rb_str_concat(detail, rb_inspect(defval)); - } - return make_inspect("WIN32OLE_PARAM", detail); -} - -void -Init_win32ole_param(void) -{ - cWIN32OLE_PARAM = rb_define_class_under(cWIN32OLE, "Param", rb_cObject); - rb_define_const(rb_cObject, "WIN32OLE_PARAM", cWIN32OLE_PARAM); - rb_define_alloc_func(cWIN32OLE_PARAM, foleparam_s_allocate); - rb_define_method(cWIN32OLE_PARAM, "initialize", foleparam_initialize, 2); - rb_define_method(cWIN32OLE_PARAM, "name", foleparam_name, 0); - rb_define_method(cWIN32OLE_PARAM, "ole_type", foleparam_ole_type, 0); - rb_define_method(cWIN32OLE_PARAM, "ole_type_detail", foleparam_ole_type_detail, 0); - rb_define_method(cWIN32OLE_PARAM, "input?", foleparam_input, 0); - rb_define_method(cWIN32OLE_PARAM, "output?", foleparam_output, 0); - rb_define_method(cWIN32OLE_PARAM, "optional?", foleparam_optional, 0); - rb_define_method(cWIN32OLE_PARAM, "retval?", foleparam_retval, 0); - rb_define_method(cWIN32OLE_PARAM, "default", foleparam_default, 0); - rb_define_alias(cWIN32OLE_PARAM, "to_s", "name"); - rb_define_method(cWIN32OLE_PARAM, "inspect", foleparam_inspect, 0); -} diff --git a/ext/win32ole/win32ole_param.h b/ext/win32ole/win32ole_param.h deleted file mode 100644 index 7e2650cb44..0000000000 --- a/ext/win32ole/win32ole_param.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef WIN32OLE_PARAM_H -#define WIN32OLE_PARAM_H - -VALUE create_win32ole_param(ITypeInfo *pTypeInfo, UINT method_index, UINT index, VALUE name); -void Init_win32ole_param(void); - -#endif - diff --git a/ext/win32ole/win32ole_record.c b/ext/win32ole/win32ole_record.c deleted file mode 100644 index 9e18653db9..0000000000 --- a/ext/win32ole/win32ole_record.c +++ /dev/null @@ -1,607 +0,0 @@ -#include "win32ole.h" - -struct olerecorddata { - IRecordInfo *pri; - void *pdata; -}; - -static HRESULT recordinfo_from_itypelib(ITypeLib *pTypeLib, VALUE name, IRecordInfo **ppri); -static int hash2olerec(VALUE key, VALUE val, VALUE rec); -static void olerecord_free(void *pvar); -static size_t olerecord_size(const void *ptr); -static VALUE folerecord_s_allocate(VALUE klass); -static VALUE folerecord_initialize(VALUE self, VALUE typename, VALUE oleobj); -static VALUE folerecord_to_h(VALUE self); -static VALUE folerecord_typename(VALUE self); -static VALUE olerecord_ivar_get(VALUE self, VALUE name); -static VALUE olerecord_ivar_set(VALUE self, VALUE name, VALUE val); -static VALUE folerecord_method_missing(int argc, VALUE *argv, VALUE self); -static VALUE folerecord_ole_instance_variable_get(VALUE self, VALUE name); -static VALUE folerecord_ole_instance_variable_set(VALUE self, VALUE name, VALUE val); -static VALUE folerecord_inspect(VALUE self); - -static const rb_data_type_t olerecord_datatype = { - "win32ole_record", - {NULL, olerecord_free, olerecord_size,}, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY -}; - -static HRESULT -recordinfo_from_itypelib(ITypeLib *pTypeLib, VALUE name, IRecordInfo **ppri) -{ - - unsigned int count; - unsigned int i; - ITypeInfo *pTypeInfo; - HRESULT hr = OLE_E_LAST; - BSTR bstr; - - count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib); - for (i = 0; i < count; i++) { - hr = pTypeLib->lpVtbl->GetDocumentation(pTypeLib, i, - &bstr, NULL, NULL, NULL); - if (FAILED(hr)) - continue; - - hr = pTypeLib->lpVtbl->GetTypeInfo(pTypeLib, i, &pTypeInfo); - if (FAILED(hr)) - continue; - - if (rb_str_cmp(WC2VSTR(bstr), name) == 0) { - hr = GetRecordInfoFromTypeInfo(pTypeInfo, ppri); - OLE_RELEASE(pTypeInfo); - return hr; - } - OLE_RELEASE(pTypeInfo); - } - hr = OLE_E_LAST; - return hr; -} - -static int -hash2olerec(VALUE key, VALUE val, VALUE rec) -{ - VARIANT var; - OLECHAR *pbuf; - struct olerecorddata *prec; - IRecordInfo *pri; - HRESULT hr; - - if (val != Qnil) { - TypedData_Get_Struct(rec, struct olerecorddata, &olerecord_datatype, prec); - pri = prec->pri; - VariantInit(&var); - ole_val2variant(val, &var); - pbuf = ole_vstr2wc(key); - hr = pri->lpVtbl->PutField(pri, INVOKE_PROPERTYPUT, prec->pdata, pbuf, &var); - SysFreeString(pbuf); - VariantClear(&var); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, "failed to putfield of `%s`", StringValuePtr(key)); - } - } - return ST_CONTINUE; -} - -void -ole_rec2variant(VALUE rec, VARIANT *var) -{ - struct olerecorddata *prec; - ULONG size = 0; - IRecordInfo *pri; - HRESULT hr; - VALUE fields; - TypedData_Get_Struct(rec, struct olerecorddata, &olerecord_datatype, prec); - pri = prec->pri; - if (pri) { - hr = pri->lpVtbl->GetSize(pri, &size); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, "failed to get size for allocation of VT_RECORD object"); - } - if (prec->pdata) { - free(prec->pdata); - } - prec->pdata = ALLOC_N(char, size); - if (!prec->pdata) { - rb_raise(rb_eRuntimeError, "failed to memory allocation of %lu bytes", (unsigned long)size); - } - hr = pri->lpVtbl->RecordInit(pri, prec->pdata); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, "failed to initialize VT_RECORD object"); - } - fields = folerecord_to_h(rec); - rb_hash_foreach(fields, hash2olerec, rec); - V_RECORDINFO(var) = pri; - V_RECORD(var) = prec->pdata; - V_VT(var) = VT_RECORD; - } else { - rb_raise(eWIN32OLERuntimeError, "failed to retrieve IRecordInfo interface"); - } -} - -void -olerecord_set_ivar(VALUE obj, IRecordInfo *pri, void *prec) -{ - HRESULT hr; - BSTR bstr; - BSTR *bstrs; - ULONG count = 0; - ULONG i; - VALUE fields; - VALUE val; - VARIANT var; - void *pdata = NULL; - struct olerecorddata *pvar; - - TypedData_Get_Struct(obj, struct olerecorddata, &olerecord_datatype, pvar); - OLE_ADDREF(pri); - OLE_RELEASE(pvar->pri); - pvar->pri = pri; - - hr = pri->lpVtbl->GetName(pri, &bstr); - if (SUCCEEDED(hr)) { - rb_ivar_set(obj, rb_intern("typename"), WC2VSTR(bstr)); - } - - hr = pri->lpVtbl->GetFieldNames(pri, &count, NULL); - if (FAILED(hr) || count == 0) - return; - bstrs = ALLOCA_N(BSTR, count); - hr = pri->lpVtbl->GetFieldNames(pri, &count, bstrs); - if (FAILED(hr)) { - return; - } - - fields = rb_hash_new(); - rb_ivar_set(obj, rb_intern("fields"), fields); - for (i = 0; i < count; i++) { - pdata = NULL; - VariantInit(&var); - val = Qnil; - if (prec) { - hr = pri->lpVtbl->GetFieldNoCopy(pri, prec, bstrs[i], &var, &pdata); - if (SUCCEEDED(hr)) { - val = ole_variant2val(&var); - } - } - rb_hash_aset(fields, WC2VSTR(bstrs[i]), val); - } -} - -VALUE -create_win32ole_record(IRecordInfo *pri, void *prec) -{ - VALUE obj = folerecord_s_allocate(cWIN32OLE_RECORD); - olerecord_set_ivar(obj, pri, prec); - return obj; -} - -/* - * Document-class: WIN32OLE_RECORD - * - * <code>WIN32OLE_RECORD</code> objects represents VT_RECORD OLE variant. - * Win32OLE returns WIN32OLE_RECORD object if the result value of invoking - * OLE methods. - * - * If COM server in VB.NET ComServer project is the following: - * - * Imports System.Runtime.InteropServices - * Public Class ComClass - * Public Structure Book - * <MarshalAs(UnmanagedType.BStr)> _ - * Public title As String - * Public cost As Integer - * End Structure - * Public Function getBook() As Book - * Dim book As New Book - * book.title = "The Ruby Book" - * book.cost = 20 - * Return book - * End Function - * End Class - * - * then, you can retrieve getBook return value from the following - * Ruby script: - * - * require 'win32ole' - * obj = WIN32OLE.new('ComServer.ComClass') - * book = obj.getBook - * book.class # => WIN32OLE_RECORD - * book.title # => "The Ruby Book" - * book.cost # => 20 - * - */ - -static void -olerecord_free(void *ptr) { - struct olerecorddata *pvar = ptr; - OLE_FREE(pvar->pri); - if (pvar->pdata) { - free(pvar->pdata); - } - free(pvar); -} - -static size_t -olerecord_size(const void *ptr) -{ - const struct olerecorddata *pvar = ptr; - size_t s = 0; - ULONG size = 0; - HRESULT hr; - if (ptr) { - s += sizeof(struct olerecorddata); - if (pvar->pri) { - hr = pvar->pri->lpVtbl->GetSize(pvar->pri, &size); - if (SUCCEEDED(hr)) { - s += size; - } - } - } - return s; -} - -static VALUE -folerecord_s_allocate(VALUE klass) { - VALUE obj = Qnil; - struct olerecorddata *pvar; - obj = TypedData_Make_Struct(klass, struct olerecorddata, &olerecord_datatype, pvar); - pvar->pri = NULL; - pvar->pdata = NULL; - return obj; -} - -/* - * call-seq: - * WIN32OLE_RECORD.new(typename, obj) -> WIN32OLE_RECORD object - * - * Returns WIN32OLE_RECORD object. The first argument is struct name (String - * or Symbol). - * The second parameter obj should be WIN32OLE object or WIN32OLE_TYPELIB object. - * If COM server in VB.NET ComServer project is the following: - * - * Imports System.Runtime.InteropServices - * Public Class ComClass - * Public Structure Book - * <MarshalAs(UnmanagedType.BStr)> _ - * Public title As String - * Public cost As Integer - * End Structure - * End Class - * - * then, you can create WIN32OLE_RECORD object is as following: - * - * require 'win32ole' - * obj = WIN32OLE.new('ComServer.ComClass') - * book1 = WIN32OLE_RECORD.new('Book', obj) # => WIN32OLE_RECORD object - * tlib = obj.ole_typelib - * book2 = WIN32OLE_RECORD.new('Book', tlib) # => WIN32OLE_RECORD object - * - */ -static VALUE -folerecord_initialize(VALUE self, VALUE typename, VALUE oleobj) { - HRESULT hr; - ITypeLib *pTypeLib = NULL; - IRecordInfo *pri = NULL; - - if (!RB_TYPE_P(typename, T_STRING) && !RB_TYPE_P(typename, T_SYMBOL)) { - rb_raise(rb_eArgError, "1st argument should be String or Symbol"); - } - if (RB_TYPE_P(typename, T_SYMBOL)) { - typename = rb_sym2str(typename); - } - - hr = S_OK; - if(rb_obj_is_kind_of(oleobj, cWIN32OLE)) { - hr = typelib_from_val(oleobj, &pTypeLib); - } else if (rb_obj_is_kind_of(oleobj, cWIN32OLE_TYPELIB)) { - pTypeLib = itypelib(oleobj); - OLE_ADDREF(pTypeLib); - if (pTypeLib) { - hr = S_OK; - } else { - hr = E_FAIL; - } - } else { - rb_raise(rb_eArgError, "2nd argument should be WIN32OLE object or WIN32OLE_TYPELIB object"); - } - - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, "fail to query ITypeLib interface"); - } - - hr = recordinfo_from_itypelib(pTypeLib, typename, &pri); - OLE_RELEASE(pTypeLib); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, "fail to query IRecordInfo interface for `%s'", StringValuePtr(typename)); - } - - olerecord_set_ivar(self, pri, NULL); - - return self; -} - -/* - * call-seq: - * WIN32OLE_RECORD#to_h #=> Ruby Hash object. - * - * Returns Ruby Hash object which represents VT_RECORD variable. - * The keys of Hash object are member names of VT_RECORD OLE variable and - * the values of Hash object are values of VT_RECORD OLE variable. - * - * If COM server in VB.NET ComServer project is the following: - * - * Imports System.Runtime.InteropServices - * Public Class ComClass - * Public Structure Book - * <MarshalAs(UnmanagedType.BStr)> _ - * Public title As String - * Public cost As Integer - * End Structure - * Public Function getBook() As Book - * Dim book As New Book - * book.title = "The Ruby Book" - * book.cost = 20 - * Return book - * End Function - * End Class - * - * then, the result of WIN32OLE_RECORD#to_h is the following: - * - * require 'win32ole' - * obj = WIN32OLE.new('ComServer.ComClass') - * book = obj.getBook - * book.to_h # => {"title"=>"The Ruby Book", "cost"=>20} - * - */ -static VALUE -folerecord_to_h(VALUE self) -{ - return rb_ivar_get(self, rb_intern("fields")); -} - -/* - * call-seq: - * WIN32OLE_RECORD#typename #=> String object - * - * Returns the type name of VT_RECORD OLE variable. - * - * If COM server in VB.NET ComServer project is the following: - * - * Imports System.Runtime.InteropServices - * Public Class ComClass - * Public Structure Book - * <MarshalAs(UnmanagedType.BStr)> _ - * Public title As String - * Public cost As Integer - * End Structure - * Public Function getBook() As Book - * Dim book As New Book - * book.title = "The Ruby Book" - * book.cost = 20 - * Return book - * End Function - * End Class - * - * then, the result of WIN32OLE_RECORD#typename is the following: - * - * require 'win32ole' - * obj = WIN32OLE.new('ComServer.ComClass') - * book = obj.getBook - * book.typename # => "Book" - * - */ -static VALUE -folerecord_typename(VALUE self) -{ - return rb_ivar_get(self, rb_intern("typename")); -} - -static VALUE -olerecord_ivar_get(VALUE self, VALUE name) -{ - VALUE fields; - fields = rb_ivar_get(self, rb_intern("fields")); - return rb_hash_fetch(fields, name); -} - -static VALUE -olerecord_ivar_set(VALUE self, VALUE name, VALUE val) -{ - long len; - char *p; - VALUE fields; - len = RSTRING_LEN(name); - p = RSTRING_PTR(name); - if (p[len-1] == '=') { - name = rb_str_subseq(name, 0, len-1); - } - fields = rb_ivar_get(self, rb_intern("fields")); - rb_hash_fetch(fields, name); - return rb_hash_aset(fields, name, val); -} - -/* - * call-seq: - * WIN32OLE_RECORD#method_missing(name) - * - * Returns value specified by the member name of VT_RECORD OLE variable. - * Or sets value specified by the member name of VT_RECORD OLE variable. - * If the member name is not correct, KeyError exception is raised. - * - * If COM server in VB.NET ComServer project is the following: - * - * Imports System.Runtime.InteropServices - * Public Class ComClass - * Public Structure Book - * <MarshalAs(UnmanagedType.BStr)> _ - * Public title As String - * Public cost As Integer - * End Structure - * End Class - * - * Then getting/setting value from Ruby is as the following: - * - * obj = WIN32OLE.new('ComServer.ComClass') - * book = WIN32OLE_RECORD.new('Book', obj) - * book.title # => nil ( book.method_missing(:title) is invoked. ) - * book.title = "Ruby" # ( book.method_missing(:title=, "Ruby") is invoked. ) - */ -static VALUE -folerecord_method_missing(int argc, VALUE *argv, VALUE self) -{ - VALUE name; - rb_check_arity(argc, 1, 2); - name = rb_sym2str(argv[0]); - -#if SIZEOF_SIZE_T > SIZEOF_LONG - { - size_t n = strlen(StringValueCStr(name)); - if (n >= LONG_MAX) { - rb_raise(rb_eRuntimeError, "too long member name"); - } - } -#endif - - if (argc == 1) { - return olerecord_ivar_get(self, name); - } else if (argc == 2) { - return olerecord_ivar_set(self, name, argv[1]); - } - return Qnil; -} - -/* - * call-seq: - * WIN32OLE_RECORD#ole_instance_variable_get(name) - * - * Returns value specified by the member name of VT_RECORD OLE object. - * If the member name is not correct, KeyError exception is raised. - * If you can't access member variable of VT_RECORD OLE object directly, - * use this method. - * - * If COM server in VB.NET ComServer project is the following: - * - * Imports System.Runtime.InteropServices - * Public Class ComClass - * Public Structure ComObject - * Public object_id As Ineger - * End Structure - * End Class - * - * and Ruby Object class has title attribute: - * - * then accessing object_id of ComObject from Ruby is as the following: - * - * srver = WIN32OLE.new('ComServer.ComClass') - * obj = WIN32OLE_RECORD.new('ComObject', server) - * # obj.object_id returns Ruby Object#object_id - * obj.ole_instance_variable_get(:object_id) # => nil - * - */ -static VALUE -folerecord_ole_instance_variable_get(VALUE self, VALUE name) -{ - VALUE sname; - if(!RB_TYPE_P(name, T_STRING) && !RB_TYPE_P(name, T_SYMBOL)) { - rb_raise(rb_eTypeError, "wrong argument type (expected String or Symbol)"); - } - sname = name; - if (RB_TYPE_P(name, T_SYMBOL)) { - sname = rb_sym2str(name); - } - return olerecord_ivar_get(self, sname); -} - -/* - * call-seq: - * WIN32OLE_RECORD#ole_instance_variable_set(name, val) - * - * Sets value specified by the member name of VT_RECORD OLE object. - * If the member name is not correct, KeyError exception is raised. - * If you can't set value of member of VT_RECORD OLE object directly, - * use this method. - * - * If COM server in VB.NET ComServer project is the following: - * - * Imports System.Runtime.InteropServices - * Public Class ComClass - * <MarshalAs(UnmanagedType.BStr)> _ - * Public title As String - * Public cost As Integer - * End Class - * - * then setting value of the `title' member is as following: - * - * srver = WIN32OLE.new('ComServer.ComClass') - * obj = WIN32OLE_RECORD.new('Book', server) - * obj.ole_instance_variable_set(:title, "The Ruby Book") - * - */ -static VALUE -folerecord_ole_instance_variable_set(VALUE self, VALUE name, VALUE val) -{ - VALUE sname; - if(!RB_TYPE_P(name, T_STRING) && !RB_TYPE_P(name, T_SYMBOL)) { - rb_raise(rb_eTypeError, "wrong argument type (expected String or Symbol)"); - } - sname = name; - if (RB_TYPE_P(name, T_SYMBOL)) { - sname = rb_sym2str(name); - } - return olerecord_ivar_set(self, sname, val); -} - -/* - * call-seq: - * WIN32OLE_RECORD#inspect -> String - * - * Returns the OLE struct name and member name and the value of member - * - * If COM server in VB.NET ComServer project is the following: - * - * Imports System.Runtime.InteropServices - * Public Class ComClass - * <MarshalAs(UnmanagedType.BStr)> _ - * Public title As String - * Public cost As Integer - * End Class - * - * then - * - * srver = WIN32OLE.new('ComServer.ComClass') - * obj = WIN32OLE_RECORD.new('Book', server) - * obj.inspect # => <WIN32OLE_RECORD(ComClass) {"title" => nil, "cost" => nil}> - * - */ -static VALUE -folerecord_inspect(VALUE self) -{ - VALUE tname; - VALUE field; - tname = folerecord_typename(self); - if (tname == Qnil) { - tname = rb_inspect(tname); - } - field = rb_inspect(folerecord_to_h(self)); - return rb_sprintf("#<WIN32OLE_RECORD(%"PRIsVALUE") %"PRIsVALUE">", - tname, - field); -} - -VALUE cWIN32OLE_RECORD; - -void -Init_win32ole_record(void) -{ - cWIN32OLE_RECORD = rb_define_class_under(cWIN32OLE, "Record", rb_cObject); - rb_define_const(rb_cObject, "WIN32OLE_RECORD", cWIN32OLE_RECORD); - rb_define_alloc_func(cWIN32OLE_RECORD, folerecord_s_allocate); - rb_define_method(cWIN32OLE_RECORD, "initialize", folerecord_initialize, 2); - rb_define_method(cWIN32OLE_RECORD, "to_h", folerecord_to_h, 0); - rb_define_method(cWIN32OLE_RECORD, "typename", folerecord_typename, 0); - rb_define_method(cWIN32OLE_RECORD, "method_missing", folerecord_method_missing, -1); - rb_define_method(cWIN32OLE_RECORD, "ole_instance_variable_get", folerecord_ole_instance_variable_get, 1); - rb_define_method(cWIN32OLE_RECORD, "ole_instance_variable_set", folerecord_ole_instance_variable_set, 2); - rb_define_method(cWIN32OLE_RECORD, "inspect", folerecord_inspect, 0); -} diff --git a/ext/win32ole/win32ole_record.h b/ext/win32ole/win32ole_record.h deleted file mode 100644 index ab1df0ee7f..0000000000 --- a/ext/win32ole/win32ole_record.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef WIN32OLE_RECORD_H -#define WIN32OLE_RECORD_H 1 - -extern VALUE cWIN32OLE_RECORD; -void ole_rec2variant(VALUE rec, VARIANT *var); -void olerecord_set_ivar(VALUE obj, IRecordInfo *pri, void *prec); -VALUE create_win32ole_record(IRecordInfo *pri, void *prec); -void Init_win32ole_record(void); - -#endif diff --git a/ext/win32ole/win32ole_type.c b/ext/win32ole/win32ole_type.c deleted file mode 100644 index 1b96aea858..0000000000 --- a/ext/win32ole/win32ole_type.c +++ /dev/null @@ -1,918 +0,0 @@ -#include "win32ole.h" - -struct oletypedata { - ITypeInfo *pTypeInfo; -}; - -static void oletype_free(void *ptr); -static size_t oletype_size(const void *ptr); -static VALUE foletype_s_ole_classes(VALUE self, VALUE typelib); -static VALUE foletype_s_typelibs(VALUE self); -static VALUE foletype_s_progids(VALUE self); -static VALUE oletype_set_member(VALUE self, ITypeInfo *pTypeInfo, VALUE name); -static VALUE foletype_s_allocate(VALUE klass); -static VALUE oleclass_from_typelib(VALUE self, ITypeLib *pTypeLib, VALUE oleclass); -static VALUE foletype_initialize(VALUE self, VALUE typelib, VALUE oleclass); -static VALUE foletype_name(VALUE self); -static VALUE ole_ole_type(ITypeInfo *pTypeInfo); -static VALUE foletype_ole_type(VALUE self); -static VALUE ole_type_guid(ITypeInfo *pTypeInfo); -static VALUE foletype_guid(VALUE self); -static VALUE ole_type_progid(ITypeInfo *pTypeInfo); -static VALUE foletype_progid(VALUE self); -static VALUE ole_type_visible(ITypeInfo *pTypeInfo); -static VALUE foletype_visible(VALUE self); -static VALUE ole_type_major_version(ITypeInfo *pTypeInfo); -static VALUE foletype_major_version(VALUE self); -static VALUE ole_type_minor_version(ITypeInfo *pTypeInfo); -static VALUE foletype_minor_version(VALUE self); -static VALUE ole_type_typekind(ITypeInfo *pTypeInfo); -static VALUE foletype_typekind(VALUE self); -static VALUE ole_type_helpstring(ITypeInfo *pTypeInfo); -static VALUE foletype_helpstring(VALUE self); -static VALUE ole_type_src_type(ITypeInfo *pTypeInfo); -static VALUE foletype_src_type(VALUE self); -static VALUE ole_type_helpfile(ITypeInfo *pTypeInfo); -static VALUE foletype_helpfile(VALUE self); -static VALUE ole_type_helpcontext(ITypeInfo *pTypeInfo); -static VALUE foletype_helpcontext(VALUE self); -static VALUE ole_variables(ITypeInfo *pTypeInfo); -static VALUE foletype_variables(VALUE self); -static VALUE foletype_methods(VALUE self); -static VALUE foletype_ole_typelib(VALUE self); -static VALUE ole_type_impl_ole_types(ITypeInfo *pTypeInfo, int implflags); -static VALUE foletype_impl_ole_types(VALUE self); -static VALUE foletype_source_ole_types(VALUE self); -static VALUE foletype_default_event_sources(VALUE self); -static VALUE foletype_default_ole_types(VALUE self); -static VALUE foletype_inspect(VALUE self); - -static const rb_data_type_t oletype_datatype = { - "win32ole_type", - {NULL, oletype_free, oletype_size,}, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY -}; - -/* - * Document-class: WIN32OLE_TYPE - * - * <code>WIN32OLE_TYPE</code> objects represent OLE type library information. - */ - -static void -oletype_free(void *ptr) -{ - struct oletypedata *poletype = ptr; - OLE_FREE(poletype->pTypeInfo); - free(poletype); -} - -static size_t -oletype_size(const void *ptr) -{ - return ptr ? sizeof(struct oletypedata) : 0; -} - -ITypeInfo *itypeinfo(VALUE self) -{ - struct oletypedata *ptype; - TypedData_Get_Struct(self, struct oletypedata, &oletype_datatype, ptype); - return ptype->pTypeInfo; -} - -VALUE -ole_type_from_itypeinfo(ITypeInfo *pTypeInfo) -{ - ITypeLib *pTypeLib; - VALUE type = Qnil; - HRESULT hr; - unsigned int index; - BSTR bstr; - - hr = pTypeInfo->lpVtbl->GetContainingTypeLib( pTypeInfo, &pTypeLib, &index ); - if(FAILED(hr)) { - return Qnil; - } - hr = pTypeLib->lpVtbl->GetDocumentation( pTypeLib, index, - &bstr, NULL, NULL, NULL); - OLE_RELEASE(pTypeLib); - if (FAILED(hr)) { - return Qnil; - } - type = create_win32ole_type(pTypeInfo, WC2VSTR(bstr)); - return type; -} - - -/* - * call-seq: - * WIN32OLE_TYPE.ole_classes(typelib) - * - * Returns array of WIN32OLE_TYPE objects defined by the <i>typelib</i> type library. - * This method will be OBSOLETE. Use WIN32OLE_TYPELIB.new(typelib).ole_classes instead. - */ -static VALUE -foletype_s_ole_classes(VALUE self, VALUE typelib) -{ - VALUE obj; - - /* - rb_warn("%s is obsolete; use %s instead.", - "WIN32OLE_TYPE.ole_classes", - "WIN32OLE_TYPELIB.new(typelib).ole_types"); - */ - obj = rb_funcall(cWIN32OLE_TYPELIB, rb_intern("new"), 1, typelib); - return rb_funcall(obj, rb_intern("ole_types"), 0); -} - -/* - * call-seq: - * WIN32OLE_TYPE.typelibs - * - * Returns array of type libraries. - * This method will be OBSOLETE. Use WIN32OLE_TYPELIB.typelibs.collect{|t| t.name} instead. - * - */ -static VALUE -foletype_s_typelibs(VALUE self) -{ - /* - rb_warn("%s is obsolete. use %s instead.", - "WIN32OLE_TYPE.typelibs", - "WIN32OLE_TYPELIB.typelibs.collect{t|t.name}"); - */ - return rb_eval_string("WIN32OLE_TYPELIB.typelibs.collect{|t|t.name}"); -} - -/* - * call-seq: - * WIN32OLE_TYPE.progids - * - * Returns array of ProgID. - */ -static VALUE -foletype_s_progids(VALUE self) -{ - HKEY hclsids, hclsid; - DWORD i; - LONG err; - VALUE clsid; - VALUE v = rb_str_new2(""); - VALUE progids = rb_ary_new(); - - err = reg_open_key(HKEY_CLASSES_ROOT, "CLSID", &hclsids); - if(err != ERROR_SUCCESS) { - return progids; - } - for(i = 0; ; i++) { - clsid = reg_enum_key(hclsids, i); - if (clsid == Qnil) - break; - err = reg_open_vkey(hclsids, clsid, &hclsid); - if (err != ERROR_SUCCESS) - continue; - if ((v = reg_get_val2(hclsid, "ProgID")) != Qnil) - rb_ary_push(progids, v); - if ((v = reg_get_val2(hclsid, "VersionIndependentProgID")) != Qnil) - rb_ary_push(progids, v); - RegCloseKey(hclsid); - } - RegCloseKey(hclsids); - return progids; -} - -static VALUE -oletype_set_member(VALUE self, ITypeInfo *pTypeInfo, VALUE name) -{ - struct oletypedata *ptype; - TypedData_Get_Struct(self, struct oletypedata, &oletype_datatype, ptype); - rb_ivar_set(self, rb_intern("name"), name); - ptype->pTypeInfo = pTypeInfo; - OLE_ADDREF(pTypeInfo); - return self; -} - -static VALUE -foletype_s_allocate(VALUE klass) -{ - struct oletypedata *poletype; - VALUE obj; - ole_initialize(); - obj = TypedData_Make_Struct(klass,struct oletypedata, &oletype_datatype, poletype); - poletype->pTypeInfo = NULL; - return obj; -} - -VALUE -create_win32ole_type(ITypeInfo *pTypeInfo, VALUE name) -{ - VALUE obj = foletype_s_allocate(cWIN32OLE_TYPE); - oletype_set_member(obj, pTypeInfo, name); - return obj; -} - -static VALUE -oleclass_from_typelib(VALUE self, ITypeLib *pTypeLib, VALUE oleclass) -{ - - long count; - int i; - HRESULT hr; - BSTR bstr; - VALUE typelib; - ITypeInfo *pTypeInfo; - - VALUE found = Qfalse; - - count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib); - for (i = 0; i < count && found == Qfalse; i++) { - hr = pTypeLib->lpVtbl->GetTypeInfo(pTypeLib, i, &pTypeInfo); - if (FAILED(hr)) - continue; - hr = pTypeLib->lpVtbl->GetDocumentation(pTypeLib, i, - &bstr, NULL, NULL, NULL); - if (FAILED(hr)) - continue; - typelib = WC2VSTR(bstr); - if (rb_str_cmp(oleclass, typelib) == 0) { - oletype_set_member(self, pTypeInfo, typelib); - found = Qtrue; - } - OLE_RELEASE(pTypeInfo); - } - return found; -} - -/* - * call-seq: - * WIN32OLE_TYPE.new(typelib, ole_class) -> WIN32OLE_TYPE object - * - * Returns a new WIN32OLE_TYPE object. - * The first argument <i>typelib</i> specifies OLE type library name. - * The second argument specifies OLE class name. - * - * WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Application') - * # => WIN32OLE_TYPE object of Application class of Excel. - */ -static VALUE -foletype_initialize(VALUE self, VALUE typelib, VALUE oleclass) -{ - VALUE file; - OLECHAR * pbuf; - ITypeLib *pTypeLib; - HRESULT hr; - - SafeStringValue(oleclass); - SafeStringValue(typelib); - file = typelib_file(typelib); - if (file == Qnil) { - file = typelib; - } - pbuf = ole_vstr2wc(file); - hr = LoadTypeLibEx(pbuf, REGKIND_NONE, &pTypeLib); - if (FAILED(hr)) - ole_raise(hr, eWIN32OLERuntimeError, "failed to LoadTypeLibEx"); - SysFreeString(pbuf); - if (oleclass_from_typelib(self, pTypeLib, oleclass) == Qfalse) { - OLE_RELEASE(pTypeLib); - rb_raise(eWIN32OLERuntimeError, "not found `%s` in `%s`", - StringValuePtr(oleclass), StringValuePtr(typelib)); - } - OLE_RELEASE(pTypeLib); - return self; -} - -/* - * call-seq: - * WIN32OLE_TYPE#name #=> OLE type name - * - * Returns OLE type name. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Application') - * puts tobj.name # => Application - */ -static VALUE -foletype_name(VALUE self) -{ - return rb_ivar_get(self, rb_intern("name")); -} - -static VALUE -ole_ole_type(ITypeInfo *pTypeInfo) -{ - HRESULT hr; - TYPEATTR *pTypeAttr; - VALUE type = Qnil; - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if(FAILED(hr)){ - return type; - } - switch(pTypeAttr->typekind) { - case TKIND_ENUM: - type = rb_str_new2("Enum"); - break; - case TKIND_RECORD: - type = rb_str_new2("Record"); - break; - case TKIND_MODULE: - type = rb_str_new2("Module"); - break; - case TKIND_INTERFACE: - type = rb_str_new2("Interface"); - break; - case TKIND_DISPATCH: - type = rb_str_new2("Dispatch"); - break; - case TKIND_COCLASS: - type = rb_str_new2("Class"); - break; - case TKIND_ALIAS: - type = rb_str_new2("Alias"); - break; - case TKIND_UNION: - type = rb_str_new2("Union"); - break; - case TKIND_MAX: - type = rb_str_new2("Max"); - break; - default: - type = Qnil; - break; - } - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - return type; -} - -/* - * call-seq: - * WIN32OLE_TYPE#ole_type #=> OLE type string. - * - * returns type of OLE class. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Application') - * puts tobj.ole_type # => Class - */ -static VALUE -foletype_ole_type(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_ole_type(pTypeInfo); -} - -static VALUE -ole_type_guid(ITypeInfo *pTypeInfo) -{ - HRESULT hr; - TYPEATTR *pTypeAttr; - int len; - OLECHAR bstr[80]; - VALUE guid = Qnil; - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if (FAILED(hr)) - return guid; - len = StringFromGUID2(&pTypeAttr->guid, bstr, sizeof(bstr)/sizeof(OLECHAR)); - if (len > 3) { - guid = ole_wc2vstr(bstr, FALSE); - } - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - return guid; -} - -/* - * call-seq: - * WIN32OLE_TYPE#guid #=> GUID - * - * Returns GUID. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Application') - * puts tobj.guid # => {00024500-0000-0000-C000-000000000046} - */ -static VALUE -foletype_guid(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_type_guid(pTypeInfo); -} - -static VALUE -ole_type_progid(ITypeInfo *pTypeInfo) -{ - HRESULT hr; - TYPEATTR *pTypeAttr; - OLECHAR *pbuf; - VALUE progid = Qnil; - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if (FAILED(hr)) - return progid; - hr = ProgIDFromCLSID(&pTypeAttr->guid, &pbuf); - if (SUCCEEDED(hr)) { - progid = ole_wc2vstr(pbuf, FALSE); - CoTaskMemFree(pbuf); - } - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - return progid; -} - -/* - * call-seq: - * WIN32OLE_TYPE#progid #=> ProgID - * - * Returns ProgID if it exists. If not found, then returns nil. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Application') - * puts tobj.progid # => Excel.Application.9 - */ -static VALUE -foletype_progid(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_type_progid(pTypeInfo); -} - - -static VALUE -ole_type_visible(ITypeInfo *pTypeInfo) -{ - HRESULT hr; - TYPEATTR *pTypeAttr; - VALUE visible; - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if (FAILED(hr)) - return Qtrue; - if (pTypeAttr->wTypeFlags & (TYPEFLAG_FHIDDEN | TYPEFLAG_FRESTRICTED)) { - visible = Qfalse; - } else { - visible = Qtrue; - } - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - return visible; -} - -/* - * call-seq: - * WIN32OLE_TYPE#visible? #=> true or false - * - * Returns true if the OLE class is public. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Application') - * puts tobj.visible # => true - */ -static VALUE -foletype_visible(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_type_visible(pTypeInfo); -} - -static VALUE -ole_type_major_version(ITypeInfo *pTypeInfo) -{ - VALUE ver; - TYPEATTR *pTypeAttr; - HRESULT hr; - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if (FAILED(hr)) - ole_raise(hr, eWIN32OLERuntimeError, "failed to GetTypeAttr"); - ver = RB_INT2FIX(pTypeAttr->wMajorVerNum); - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - return ver; -} - -/* - * call-seq: - * WIN32OLE_TYPE#major_version - * - * Returns major version. - * tobj = WIN32OLE_TYPE.new('Microsoft Word 10.0 Object Library', 'Documents') - * puts tobj.major_version # => 8 - */ -static VALUE -foletype_major_version(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_type_major_version(pTypeInfo); -} - -static VALUE -ole_type_minor_version(ITypeInfo *pTypeInfo) -{ - VALUE ver; - TYPEATTR *pTypeAttr; - HRESULT hr; - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if (FAILED(hr)) - ole_raise(hr, eWIN32OLERuntimeError, "failed to GetTypeAttr"); - ver = RB_INT2FIX(pTypeAttr->wMinorVerNum); - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - return ver; -} - -/* - * call-seq: - * WIN32OLE_TYPE#minor_version #=> OLE minor version - * - * Returns minor version. - * tobj = WIN32OLE_TYPE.new('Microsoft Word 10.0 Object Library', 'Documents') - * puts tobj.minor_version # => 2 - */ -static VALUE -foletype_minor_version(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_type_minor_version(pTypeInfo); -} - -static VALUE -ole_type_typekind(ITypeInfo *pTypeInfo) -{ - VALUE typekind; - TYPEATTR *pTypeAttr; - HRESULT hr; - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if (FAILED(hr)) - ole_raise(hr, eWIN32OLERuntimeError, "failed to GetTypeAttr"); - typekind = RB_INT2FIX(pTypeAttr->typekind); - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - return typekind; -} - -/* - * call-seq: - * WIN32OLE_TYPE#typekind #=> number of type. - * - * Returns number which represents type. - * tobj = WIN32OLE_TYPE.new('Microsoft Word 10.0 Object Library', 'Documents') - * puts tobj.typekind # => 4 - * - */ -static VALUE -foletype_typekind(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_type_typekind(pTypeInfo); -} - -static VALUE -ole_type_helpstring(ITypeInfo *pTypeInfo) -{ - HRESULT hr; - BSTR bhelpstr; - hr = ole_docinfo_from_type(pTypeInfo, NULL, &bhelpstr, NULL, NULL); - if(FAILED(hr)) { - return Qnil; - } - return WC2VSTR(bhelpstr); -} - -/* - * call-seq: - * WIN32OLE_TYPE#helpstring #=> help string. - * - * Returns help string. - * tobj = WIN32OLE_TYPE.new('Microsoft Internet Controls', 'IWebBrowser') - * puts tobj.helpstring # => Web Browser interface - */ -static VALUE -foletype_helpstring(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_type_helpstring(pTypeInfo); -} - -static VALUE -ole_type_src_type(ITypeInfo *pTypeInfo) -{ - HRESULT hr; - TYPEATTR *pTypeAttr; - VALUE alias = Qnil; - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if (FAILED(hr)) - return alias; - if(pTypeAttr->typekind != TKIND_ALIAS) { - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - return alias; - } - alias = ole_typedesc2val(pTypeInfo, &(pTypeAttr->tdescAlias), Qnil); - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - return alias; -} - -/* - * call-seq: - * WIN32OLE_TYPE#src_type #=> OLE source class - * - * Returns source class when the OLE class is 'Alias'. - * tobj = WIN32OLE_TYPE.new('Microsoft Office 9.0 Object Library', 'MsoRGBType') - * puts tobj.src_type # => I4 - * - */ -static VALUE -foletype_src_type(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_type_src_type(pTypeInfo); -} - -static VALUE -ole_type_helpfile(ITypeInfo *pTypeInfo) -{ - HRESULT hr; - BSTR bhelpfile; - hr = ole_docinfo_from_type(pTypeInfo, NULL, NULL, NULL, &bhelpfile); - if(FAILED(hr)) { - return Qnil; - } - return WC2VSTR(bhelpfile); -} - -/* - * call-seq: - * WIN32OLE_TYPE#helpfile - * - * Returns helpfile path. If helpfile is not found, then returns nil. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Worksheet') - * puts tobj.helpfile # => C:\...\VBAXL9.CHM - * - */ -static VALUE -foletype_helpfile(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_type_helpfile(pTypeInfo); -} - -static VALUE -ole_type_helpcontext(ITypeInfo *pTypeInfo) -{ - HRESULT hr; - DWORD helpcontext; - hr = ole_docinfo_from_type(pTypeInfo, NULL, NULL, - &helpcontext, NULL); - if(FAILED(hr)) - return Qnil; - return RB_INT2FIX(helpcontext); -} - -/* - * call-seq: - * WIN32OLE_TYPE#helpcontext - * - * Returns helpcontext. If helpcontext is not found, then returns nil. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Worksheet') - * puts tobj.helpfile # => 131185 - */ -static VALUE -foletype_helpcontext(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_type_helpcontext(pTypeInfo); -} - -static VALUE -ole_variables(ITypeInfo *pTypeInfo) -{ - HRESULT hr; - TYPEATTR *pTypeAttr; - WORD i; - UINT len; - BSTR bstr; - VARDESC *pVarDesc; - VALUE var; - VALUE variables = rb_ary_new(); - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, "failed to GetTypeAttr"); - } - - for(i = 0; i < pTypeAttr->cVars; i++) { - hr = pTypeInfo->lpVtbl->GetVarDesc(pTypeInfo, i, &pVarDesc); - if(FAILED(hr)) - continue; - len = 0; - hr = pTypeInfo->lpVtbl->GetNames(pTypeInfo, pVarDesc->memid, &bstr, - 1, &len); - if(FAILED(hr) || len == 0 || !bstr) - continue; - - var = create_win32ole_variable(pTypeInfo, i, WC2VSTR(bstr)); - rb_ary_push(variables, var); - - pTypeInfo->lpVtbl->ReleaseVarDesc(pTypeInfo, pVarDesc); - pVarDesc = NULL; - } - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - return variables; -} - -/* - * call-seq: - * WIN32OLE_TYPE#variables - * - * Returns array of WIN32OLE_VARIABLE objects which represent variables - * defined in OLE class. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'XlSheetType') - * vars = tobj.variables - * vars.each do |v| - * puts "#{v.name} = #{v.value}" - * end - * - * The result of above sample script is follows: - * xlChart = -4109 - * xlDialogSheet = -4116 - * xlExcel4IntlMacroSheet = 4 - * xlExcel4MacroSheet = 3 - * xlWorksheet = -4167 - * - */ -static VALUE -foletype_variables(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_variables(pTypeInfo); -} - -/* - * call-seq: - * WIN32OLE_TYPE#ole_methods # the array of WIN32OLE_METHOD objects. - * - * Returns array of WIN32OLE_METHOD objects which represent OLE method defined in - * OLE type library. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Worksheet') - * methods = tobj.ole_methods.collect{|m| - * m.name - * } - * # => ['Activate', 'Copy', 'Delete',....] - */ -static VALUE -foletype_methods(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_methods_from_typeinfo(pTypeInfo, INVOKE_FUNC | INVOKE_PROPERTYGET | INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF); -} - -/* - * call-seq: - * WIN32OLE_TYPE#ole_typelib - * - * Returns the WIN32OLE_TYPELIB object which is including the WIN32OLE_TYPE - * object. If it is not found, then returns nil. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Worksheet') - * puts tobj.ole_typelib # => 'Microsoft Excel 9.0 Object Library' - */ -static VALUE -foletype_ole_typelib(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_typelib_from_itypeinfo(pTypeInfo); -} - -static VALUE -ole_type_impl_ole_types(ITypeInfo *pTypeInfo, int implflags) -{ - HRESULT hr; - ITypeInfo *pRefTypeInfo; - HREFTYPE href; - WORD i; - VALUE type; - TYPEATTR *pTypeAttr; - int flags; - - VALUE types = rb_ary_new(); - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if (FAILED(hr)) { - return types; - } - for (i = 0; i < pTypeAttr->cImplTypes; i++) { - hr = pTypeInfo->lpVtbl->GetImplTypeFlags(pTypeInfo, i, &flags); - if (FAILED(hr)) - continue; - - hr = pTypeInfo->lpVtbl->GetRefTypeOfImplType(pTypeInfo, i, &href); - if (FAILED(hr)) - continue; - hr = pTypeInfo->lpVtbl->GetRefTypeInfo(pTypeInfo, href, &pRefTypeInfo); - if (FAILED(hr)) - continue; - - if ((flags & implflags) == implflags) { - type = ole_type_from_itypeinfo(pRefTypeInfo); - if (type != Qnil) { - rb_ary_push(types, type); - } - } - - OLE_RELEASE(pRefTypeInfo); - } - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - return types; -} - -/* - * call-seq: - * WIN32OLE_TYPE#implemented_ole_types - * - * Returns the array of WIN32OLE_TYPE object which is implemented by the WIN32OLE_TYPE - * object. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Worksheet') - * p tobj.implemented_ole_types # => [_Worksheet, DocEvents] - */ -static VALUE -foletype_impl_ole_types(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_type_impl_ole_types(pTypeInfo, 0); -} - -/* - * call-seq: - * WIN32OLE_TYPE#source_ole_types - * - * Returns the array of WIN32OLE_TYPE object which is implemented by the WIN32OLE_TYPE - * object and having IMPLTYPEFLAG_FSOURCE. - * tobj = WIN32OLE_TYPE.new('Microsoft Internet Controls', "InternetExplorer") - * p tobj.source_ole_types - * # => [#<WIN32OLE_TYPE:DWebBrowserEvents2>, #<WIN32OLE_TYPE:DWebBrowserEvents>] - */ -static VALUE -foletype_source_ole_types(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_type_impl_ole_types(pTypeInfo, IMPLTYPEFLAG_FSOURCE); -} - -/* - * call-seq: - * WIN32OLE_TYPE#default_event_sources - * - * Returns the array of WIN32OLE_TYPE object which is implemented by the WIN32OLE_TYPE - * object and having IMPLTYPEFLAG_FSOURCE and IMPLTYPEFLAG_FDEFAULT. - * tobj = WIN32OLE_TYPE.new('Microsoft Internet Controls', "InternetExplorer") - * p tobj.default_event_sources # => [#<WIN32OLE_TYPE:DWebBrowserEvents2>] - */ -static VALUE -foletype_default_event_sources(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_type_impl_ole_types(pTypeInfo, IMPLTYPEFLAG_FSOURCE|IMPLTYPEFLAG_FDEFAULT); -} - -/* - * call-seq: - * WIN32OLE_TYPE#default_ole_types - * - * Returns the array of WIN32OLE_TYPE object which is implemented by the WIN32OLE_TYPE - * object and having IMPLTYPEFLAG_FDEFAULT. - * tobj = WIN32OLE_TYPE.new('Microsoft Internet Controls', "InternetExplorer") - * p tobj.default_ole_types - * # => [#<WIN32OLE_TYPE:IWebBrowser2>, #<WIN32OLE_TYPE:DWebBrowserEvents2>] - */ -static VALUE -foletype_default_ole_types(VALUE self) -{ - ITypeInfo *pTypeInfo = itypeinfo(self); - return ole_type_impl_ole_types(pTypeInfo, IMPLTYPEFLAG_FDEFAULT); -} - -/* - * call-seq: - * WIN32OLE_TYPE#inspect -> String - * - * Returns the type name with class name. - * - * ie = WIN32OLE.new('InternetExplorer.Application') - * ie.ole_type.inspect => #<WIN32OLE_TYPE:IWebBrowser2> - */ -static VALUE -foletype_inspect(VALUE self) -{ - return default_inspect(self, "WIN32OLE_TYPE"); -} - -VALUE cWIN32OLE_TYPE; - -void Init_win32ole_type(void) -{ - cWIN32OLE_TYPE = rb_define_class_under(cWIN32OLE, "Type", rb_cObject); - rb_define_const(rb_cObject, "WIN32OLE_TYPE", cWIN32OLE_TYPE); - rb_define_singleton_method(cWIN32OLE_TYPE, "ole_classes", foletype_s_ole_classes, 1); - rb_define_singleton_method(cWIN32OLE_TYPE, "typelibs", foletype_s_typelibs, 0); - rb_define_singleton_method(cWIN32OLE_TYPE, "progids", foletype_s_progids, 0); - rb_define_alloc_func(cWIN32OLE_TYPE, foletype_s_allocate); - rb_define_method(cWIN32OLE_TYPE, "initialize", foletype_initialize, 2); - rb_define_method(cWIN32OLE_TYPE, "name", foletype_name, 0); - rb_define_method(cWIN32OLE_TYPE, "ole_type", foletype_ole_type, 0); - rb_define_method(cWIN32OLE_TYPE, "guid", foletype_guid, 0); - rb_define_method(cWIN32OLE_TYPE, "progid", foletype_progid, 0); - rb_define_method(cWIN32OLE_TYPE, "visible?", foletype_visible, 0); - rb_define_alias(cWIN32OLE_TYPE, "to_s", "name"); - rb_define_method(cWIN32OLE_TYPE, "major_version", foletype_major_version, 0); - rb_define_method(cWIN32OLE_TYPE, "minor_version", foletype_minor_version, 0); - rb_define_method(cWIN32OLE_TYPE, "typekind", foletype_typekind, 0); - rb_define_method(cWIN32OLE_TYPE, "helpstring", foletype_helpstring, 0); - rb_define_method(cWIN32OLE_TYPE, "src_type", foletype_src_type, 0); - rb_define_method(cWIN32OLE_TYPE, "helpfile", foletype_helpfile, 0); - rb_define_method(cWIN32OLE_TYPE, "helpcontext", foletype_helpcontext, 0); - rb_define_method(cWIN32OLE_TYPE, "variables", foletype_variables, 0); - rb_define_method(cWIN32OLE_TYPE, "ole_methods", foletype_methods, 0); - rb_define_method(cWIN32OLE_TYPE, "ole_typelib", foletype_ole_typelib, 0); - rb_define_method(cWIN32OLE_TYPE, "implemented_ole_types", foletype_impl_ole_types, 0); - rb_define_method(cWIN32OLE_TYPE, "source_ole_types", foletype_source_ole_types, 0); - rb_define_method(cWIN32OLE_TYPE, "default_event_sources", foletype_default_event_sources, 0); - rb_define_method(cWIN32OLE_TYPE, "default_ole_types", foletype_default_ole_types, 0); - rb_define_method(cWIN32OLE_TYPE, "inspect", foletype_inspect, 0); -} diff --git a/ext/win32ole/win32ole_type.h b/ext/win32ole/win32ole_type.h deleted file mode 100644 index 87b551e502..0000000000 --- a/ext/win32ole/win32ole_type.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef WIN32OLE_TYPE_H -#define WIN32OLE_TYPE_H 1 -extern VALUE cWIN32OLE_TYPE; -VALUE create_win32ole_type(ITypeInfo *pTypeInfo, VALUE name); -ITypeInfo *itypeinfo(VALUE self); -VALUE ole_type_from_itypeinfo(ITypeInfo *pTypeInfo); -void Init_win32ole_type(void); -#endif diff --git a/ext/win32ole/win32ole_typelib.c b/ext/win32ole/win32ole_typelib.c deleted file mode 100644 index fb68bebda8..0000000000 --- a/ext/win32ole/win32ole_typelib.c +++ /dev/null @@ -1,847 +0,0 @@ -#include "win32ole.h" - -struct oletypelibdata { - ITypeLib *pTypeLib; -}; - -static VALUE reg_get_typelib_file_path(HKEY hkey); -static VALUE oletypelib_path(VALUE guid, VALUE version); -static HRESULT oletypelib_from_guid(VALUE guid, VALUE version, ITypeLib **ppTypeLib); -static VALUE foletypelib_s_typelibs(VALUE self); -static VALUE oletypelib_set_member(VALUE self, ITypeLib *pTypeLib); -static void oletypelib_free(void *ptr); -static size_t oletypelib_size(const void *ptr); -static VALUE foletypelib_s_allocate(VALUE klass); -static VALUE oletypelib_search_registry(VALUE self, VALUE typelib); -static void oletypelib_get_libattr(ITypeLib *pTypeLib, TLIBATTR **ppTLibAttr); -static VALUE oletypelib_search_registry2(VALUE self, VALUE args); -static VALUE foletypelib_initialize(VALUE self, VALUE args); -static VALUE foletypelib_guid(VALUE self); -static VALUE foletypelib_name(VALUE self); -static VALUE make_version_str(VALUE major, VALUE minor); -static VALUE foletypelib_version(VALUE self); -static VALUE foletypelib_major_version(VALUE self); -static VALUE foletypelib_minor_version(VALUE self); -static VALUE foletypelib_path(VALUE self); -static VALUE foletypelib_visible(VALUE self); -static VALUE foletypelib_library_name(VALUE self); -static VALUE ole_types_from_typelib(ITypeLib *pTypeLib, VALUE classes); -static VALUE typelib_file_from_typelib(VALUE ole); -static VALUE typelib_file_from_clsid(VALUE ole); -static VALUE foletypelib_ole_types(VALUE self); -static VALUE foletypelib_inspect(VALUE self); - -static const rb_data_type_t oletypelib_datatype = { - "win32ole_typelib", - {NULL, oletypelib_free, oletypelib_size,}, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY -}; - -static VALUE -reg_get_typelib_file_path(HKEY hkey) -{ - VALUE path = Qnil; - path = reg_get_val2(hkey, "win64"); - if (path != Qnil) { - return path; - } - path = reg_get_val2(hkey, "win32"); - if (path != Qnil) { - return path; - } - path = reg_get_val2(hkey, "win16"); - return path; -} - -static VALUE -oletypelib_path(VALUE guid, VALUE version) -{ - int k; - LONG err; - HKEY hkey; - HKEY hlang; - VALUE lang; - VALUE path = Qnil; - - VALUE key = rb_str_new2("TypeLib\\"); - rb_str_concat(key, guid); - rb_str_cat2(key, "\\"); - rb_str_concat(key, version); - - err = reg_open_vkey(HKEY_CLASSES_ROOT, key, &hkey); - if (err != ERROR_SUCCESS) { - return Qnil; - } - for(k = 0; path == Qnil; k++) { - lang = reg_enum_key(hkey, k); - if (lang == Qnil) - break; - err = reg_open_vkey(hkey, lang, &hlang); - if (err == ERROR_SUCCESS) { - path = reg_get_typelib_file_path(hlang); - RegCloseKey(hlang); - } - } - RegCloseKey(hkey); - return path; -} - -static HRESULT -oletypelib_from_guid(VALUE guid, VALUE version, ITypeLib **ppTypeLib) -{ - VALUE path; - OLECHAR *pBuf; - HRESULT hr; - path = oletypelib_path(guid, version); - if (path == Qnil) { - return E_UNEXPECTED; - } - pBuf = ole_vstr2wc(path); - hr = LoadTypeLibEx(pBuf, REGKIND_NONE, ppTypeLib); - SysFreeString(pBuf); - return hr; -} - -ITypeLib * -itypelib(VALUE self) -{ - struct oletypelibdata *ptlib; - TypedData_Get_Struct(self, struct oletypelibdata, &oletypelib_datatype, ptlib); - return ptlib->pTypeLib; -} - -VALUE -ole_typelib_from_itypeinfo(ITypeInfo *pTypeInfo) -{ - HRESULT hr; - ITypeLib *pTypeLib; - unsigned int index; - VALUE retval = Qnil; - - hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, &pTypeLib, &index); - if(FAILED(hr)) { - return Qnil; - } - retval = create_win32ole_typelib(pTypeLib); - return retval; -} - -/* - * Document-class: WIN32OLE_TYPELIB - * - * <code>WIN32OLE_TYPELIB</code> objects represent OLE tyblib information. - */ - -/* - * call-seq: - * - * WIN32OLE_TYPELIB.typelibs - * - * Returns the array of WIN32OLE_TYPELIB object. - * - * tlibs = WIN32OLE_TYPELIB.typelibs - * - */ -static VALUE -foletypelib_s_typelibs(VALUE self) -{ - HKEY htypelib, hguid; - DWORD i, j; - LONG err; - VALUE guid; - VALUE version; - VALUE name = Qnil; - VALUE typelibs = rb_ary_new(); - VALUE typelib = Qnil; - HRESULT hr; - ITypeLib *pTypeLib; - - err = reg_open_key(HKEY_CLASSES_ROOT, "TypeLib", &htypelib); - if(err != ERROR_SUCCESS) { - return typelibs; - } - for(i = 0; ; i++) { - guid = reg_enum_key(htypelib, i); - if (guid == Qnil) - break; - err = reg_open_vkey(htypelib, guid, &hguid); - if (err != ERROR_SUCCESS) - continue; - for(j = 0; ; j++) { - version = reg_enum_key(hguid, j); - if (version == Qnil) - break; - if ( (name = reg_get_val2(hguid, StringValuePtr(version))) != Qnil ) { - hr = oletypelib_from_guid(guid, version, &pTypeLib); - if (SUCCEEDED(hr)) { - typelib = create_win32ole_typelib(pTypeLib); - rb_ary_push(typelibs, typelib); - } - } - } - RegCloseKey(hguid); - } - RegCloseKey(htypelib); - return typelibs; -} - -static VALUE -oletypelib_set_member(VALUE self, ITypeLib *pTypeLib) -{ - struct oletypelibdata *ptlib; - TypedData_Get_Struct(self, struct oletypelibdata, &oletypelib_datatype, ptlib); - ptlib->pTypeLib = pTypeLib; - return self; -} - -static void -oletypelib_free(void *ptr) -{ - struct oletypelibdata *poletypelib = ptr; - OLE_FREE(poletypelib->pTypeLib); - free(poletypelib); -} - -static size_t -oletypelib_size(const void *ptr) -{ - return ptr ? sizeof(struct oletypelibdata) : 0; -} - -static VALUE -foletypelib_s_allocate(VALUE klass) -{ - struct oletypelibdata *poletypelib; - VALUE obj; - ole_initialize(); - obj = TypedData_Make_Struct(klass, struct oletypelibdata, &oletypelib_datatype, poletypelib); - poletypelib->pTypeLib = NULL; - return obj; -} - -VALUE -create_win32ole_typelib(ITypeLib *pTypeLib) -{ - VALUE obj = foletypelib_s_allocate(cWIN32OLE_TYPELIB); - oletypelib_set_member(obj, pTypeLib); - return obj; -} - -static VALUE -oletypelib_search_registry(VALUE self, VALUE typelib) -{ - HKEY htypelib, hguid, hversion; - DWORD i, j; - LONG err; - VALUE found = Qfalse; - VALUE tlib; - VALUE guid; - VALUE ver; - HRESULT hr; - ITypeLib *pTypeLib; - - err = reg_open_key(HKEY_CLASSES_ROOT, "TypeLib", &htypelib); - if(err != ERROR_SUCCESS) { - return Qfalse; - } - for(i = 0; !found; i++) { - guid = reg_enum_key(htypelib, i); - if (guid == Qnil) - break; - err = reg_open_vkey(htypelib, guid, &hguid); - if (err != ERROR_SUCCESS) - continue; - for(j = 0; found == Qfalse; j++) { - ver = reg_enum_key(hguid, j); - if (ver == Qnil) - break; - err = reg_open_vkey(hguid, ver, &hversion); - if (err != ERROR_SUCCESS) - continue; - tlib = reg_get_val(hversion, NULL); - if (tlib == Qnil) { - RegCloseKey(hversion); - continue; - } - if (rb_str_cmp(typelib, tlib) == 0) { - hr = oletypelib_from_guid(guid, ver, &pTypeLib); - if (SUCCEEDED(hr)) { - oletypelib_set_member(self, pTypeLib); - found = Qtrue; - } - } - RegCloseKey(hversion); - } - RegCloseKey(hguid); - } - RegCloseKey(htypelib); - return found; -} - -static void -oletypelib_get_libattr(ITypeLib *pTypeLib, TLIBATTR **ppTLibAttr) -{ - HRESULT hr; - hr = pTypeLib->lpVtbl->GetLibAttr(pTypeLib, ppTLibAttr); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, - "failed to get library attribute(TLIBATTR) from ITypeLib"); - } -} - -static VALUE -oletypelib_search_registry2(VALUE self, VALUE args) -{ - HKEY htypelib, hguid, hversion; - double fver; - DWORD j; - LONG err; - VALUE found = Qfalse; - VALUE tlib; - VALUE ver; - VALUE version_str; - VALUE version = Qnil; - VALUE typelib = Qnil; - HRESULT hr; - ITypeLib *pTypeLib; - - VALUE guid = rb_ary_entry(args, 0); - version_str = make_version_str(rb_ary_entry(args, 1), rb_ary_entry(args, 2)); - - err = reg_open_key(HKEY_CLASSES_ROOT, "TypeLib", &htypelib); - if(err != ERROR_SUCCESS) { - return Qfalse; - } - err = reg_open_vkey(htypelib, guid, &hguid); - if (err != ERROR_SUCCESS) { - RegCloseKey(htypelib); - return Qfalse; - } - if (version_str != Qnil) { - err = reg_open_vkey(hguid, version_str, &hversion); - if (err == ERROR_SUCCESS) { - tlib = reg_get_val(hversion, NULL); - if (tlib != Qnil) { - typelib = tlib; - version = version_str; - } - } - RegCloseKey(hversion); - } else { - fver = 0.0; - for(j = 0; ;j++) { - ver = reg_enum_key(hguid, j); - if (ver == Qnil) - break; - err = reg_open_vkey(hguid, ver, &hversion); - if (err != ERROR_SUCCESS) - continue; - tlib = reg_get_val(hversion, NULL); - if (tlib == Qnil) { - RegCloseKey(hversion); - continue; - } - if (fver < atof(StringValuePtr(ver))) { - fver = atof(StringValuePtr(ver)); - version = ver; - typelib = tlib; - } - RegCloseKey(hversion); - } - } - RegCloseKey(hguid); - RegCloseKey(htypelib); - if (typelib != Qnil) { - hr = oletypelib_from_guid(guid, version, &pTypeLib); - if (SUCCEEDED(hr)) { - found = Qtrue; - oletypelib_set_member(self, pTypeLib); - } - } - return found; -} - - -/* - * call-seq: - * WIN32OLE_TYPELIB.new(typelib [, version1, version2]) -> WIN32OLE_TYPELIB object - * - * Returns a new WIN32OLE_TYPELIB object. - * - * The first argument <i>typelib</i> specifies OLE type library name or GUID or - * OLE library file. - * The second argument is major version or version of the type library. - * The third argument is minor version. - * The second argument and third argument are optional. - * If the first argument is type library name, then the second and third argument - * are ignored. - * - * tlib1 = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library') - * tlib2 = WIN32OLE_TYPELIB.new('{00020813-0000-0000-C000-000000000046}') - * tlib3 = WIN32OLE_TYPELIB.new('{00020813-0000-0000-C000-000000000046}', 1.3) - * tlib4 = WIN32OLE_TYPELIB.new('{00020813-0000-0000-C000-000000000046}', 1, 3) - * tlib5 = WIN32OLE_TYPELIB.new("C:\\WINNT\\SYSTEM32\\SHELL32.DLL") - * puts tlib1.name # -> 'Microsoft Excel 9.0 Object Library' - * puts tlib2.name # -> 'Microsoft Excel 9.0 Object Library' - * puts tlib3.name # -> 'Microsoft Excel 9.0 Object Library' - * puts tlib4.name # -> 'Microsoft Excel 9.0 Object Library' - * puts tlib5.name # -> 'Microsoft Shell Controls And Automation' - * - */ -static VALUE -foletypelib_initialize(VALUE self, VALUE args) -{ - VALUE found = Qfalse; - VALUE typelib = Qnil; - int len = 0; - OLECHAR * pbuf; - ITypeLib *pTypeLib; - HRESULT hr = S_OK; - - len = RARRAY_LEN(args); - rb_check_arity(len, 1, 3); - - typelib = rb_ary_entry(args, 0); - - SafeStringValue(typelib); - - found = oletypelib_search_registry(self, typelib); - if (found == Qfalse) { - found = oletypelib_search_registry2(self, args); - } - if (found == Qfalse) { - pbuf = ole_vstr2wc(typelib); - hr = LoadTypeLibEx(pbuf, REGKIND_NONE, &pTypeLib); - SysFreeString(pbuf); - if (SUCCEEDED(hr)) { - found = Qtrue; - oletypelib_set_member(self, pTypeLib); - } - } - - if (found == Qfalse) { - rb_raise(eWIN32OLERuntimeError, "not found type library `%s`", - StringValuePtr(typelib)); - } - return self; -} - -/* - * call-seq: - * WIN32OLE_TYPELIB#guid -> The guid string. - * - * Returns guid string which specifies type library. - * - * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library') - * guid = tlib.guid # -> '{00020813-0000-0000-C000-000000000046}' - */ -static VALUE -foletypelib_guid(VALUE self) -{ - ITypeLib *pTypeLib; - OLECHAR bstr[80]; - VALUE guid = Qnil; - int len; - TLIBATTR *pTLibAttr; - - pTypeLib = itypelib(self); - oletypelib_get_libattr(pTypeLib, &pTLibAttr); - len = StringFromGUID2(&pTLibAttr->guid, bstr, sizeof(bstr)/sizeof(OLECHAR)); - if (len > 3) { - guid = ole_wc2vstr(bstr, FALSE); - } - pTypeLib->lpVtbl->ReleaseTLibAttr(pTypeLib, pTLibAttr); - return guid; -} - -/* - * call-seq: - * WIN32OLE_TYPELIB#name -> The type library name - * - * Returns the type library name. - * - * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library') - * name = tlib.name # -> 'Microsoft Excel 9.0 Object Library' - */ -static VALUE -foletypelib_name(VALUE self) -{ - ITypeLib *pTypeLib; - HRESULT hr; - BSTR bstr; - VALUE name; - pTypeLib = itypelib(self); - hr = pTypeLib->lpVtbl->GetDocumentation(pTypeLib, -1, - NULL, &bstr, NULL, NULL); - - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, "failed to get name from ITypeLib"); - } - name = WC2VSTR(bstr); - return name; -} - -static VALUE -make_version_str(VALUE major, VALUE minor) -{ - VALUE version_str = Qnil; - VALUE minor_str = Qnil; - if (major == Qnil) { - return Qnil; - } - version_str = rb_String(major); - if (minor != Qnil) { - minor_str = rb_String(minor); - rb_str_cat2(version_str, "."); - rb_str_append(version_str, minor_str); - } - return version_str; -} - -/* - * call-seq: - * WIN32OLE_TYPELIB#version -> The type library version String object. - * - * Returns the type library version. - * - * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library') - * puts tlib.version #-> "1.3" - */ -static VALUE -foletypelib_version(VALUE self) -{ - TLIBATTR *pTLibAttr; - ITypeLib *pTypeLib; - VALUE version; - - pTypeLib = itypelib(self); - oletypelib_get_libattr(pTypeLib, &pTLibAttr); - version = rb_sprintf("%d.%d", pTLibAttr->wMajorVerNum, pTLibAttr->wMinorVerNum); - pTypeLib->lpVtbl->ReleaseTLibAttr(pTypeLib, pTLibAttr); - return version; -} - -/* - * call-seq: - * WIN32OLE_TYPELIB#major_version -> The type library major version. - * - * Returns the type library major version. - * - * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library') - * puts tlib.major_version # -> 1 - */ -static VALUE -foletypelib_major_version(VALUE self) -{ - TLIBATTR *pTLibAttr; - VALUE major; - ITypeLib *pTypeLib; - pTypeLib = itypelib(self); - oletypelib_get_libattr(pTypeLib, &pTLibAttr); - - major = RB_INT2NUM(pTLibAttr->wMajorVerNum); - pTypeLib->lpVtbl->ReleaseTLibAttr(pTypeLib, pTLibAttr); - return major; -} - -/* - * call-seq: - * WIN32OLE_TYPELIB#minor_version -> The type library minor version. - * - * Returns the type library minor version. - * - * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library') - * puts tlib.minor_version # -> 3 - */ -static VALUE -foletypelib_minor_version(VALUE self) -{ - TLIBATTR *pTLibAttr; - VALUE minor; - ITypeLib *pTypeLib; - pTypeLib = itypelib(self); - oletypelib_get_libattr(pTypeLib, &pTLibAttr); - minor = RB_INT2NUM(pTLibAttr->wMinorVerNum); - pTypeLib->lpVtbl->ReleaseTLibAttr(pTypeLib, pTLibAttr); - return minor; -} - -/* - * call-seq: - * WIN32OLE_TYPELIB#path -> The type library file path. - * - * Returns the type library file path. - * - * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library') - * puts tlib.path #-> 'C:\...\EXCEL9.OLB' - */ -static VALUE -foletypelib_path(VALUE self) -{ - TLIBATTR *pTLibAttr; - HRESULT hr = S_OK; - BSTR bstr; - LCID lcid = cWIN32OLE_lcid; - VALUE path; - ITypeLib *pTypeLib; - - pTypeLib = itypelib(self); - oletypelib_get_libattr(pTypeLib, &pTLibAttr); - hr = QueryPathOfRegTypeLib(&pTLibAttr->guid, - pTLibAttr->wMajorVerNum, - pTLibAttr->wMinorVerNum, - lcid, - &bstr); - if (FAILED(hr)) { - pTypeLib->lpVtbl->ReleaseTLibAttr(pTypeLib, pTLibAttr); - ole_raise(hr, eWIN32OLERuntimeError, "failed to QueryPathOfRegTypeTypeLib"); - } - - pTypeLib->lpVtbl->ReleaseTLibAttr(pTypeLib, pTLibAttr); - path = WC2VSTR(bstr); - return path; -} - -/* - * call-seq: - * WIN32OLE_TYPELIB#visible? - * - * Returns true if the type library information is not hidden. - * If wLibFlags of TLIBATTR is 0 or LIBFLAG_FRESTRICTED or LIBFLAG_FHIDDEN, - * the method returns false, otherwise, returns true. - * If the method fails to access the TLIBATTR information, then - * WIN32OLERuntimeError is raised. - * - * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library') - * tlib.visible? # => true - */ -static VALUE -foletypelib_visible(VALUE self) -{ - ITypeLib *pTypeLib = NULL; - VALUE visible = Qtrue; - TLIBATTR *pTLibAttr; - - pTypeLib = itypelib(self); - oletypelib_get_libattr(pTypeLib, &pTLibAttr); - - if ((pTLibAttr->wLibFlags == 0) || - (pTLibAttr->wLibFlags & LIBFLAG_FRESTRICTED) || - (pTLibAttr->wLibFlags & LIBFLAG_FHIDDEN)) { - visible = Qfalse; - } - pTypeLib->lpVtbl->ReleaseTLibAttr(pTypeLib, pTLibAttr); - return visible; -} - -/* - * call-seq: - * WIN32OLE_TYPELIB#library_name - * - * Returns library name. - * If the method fails to access library name, WIN32OLERuntimeError is raised. - * - * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library') - * tlib.library_name # => Excel - */ -static VALUE -foletypelib_library_name(VALUE self) -{ - HRESULT hr; - ITypeLib *pTypeLib = NULL; - VALUE libname = Qnil; - BSTR bstr; - - pTypeLib = itypelib(self); - hr = pTypeLib->lpVtbl->GetDocumentation(pTypeLib, -1, - &bstr, NULL, NULL, NULL); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, "failed to get library name"); - } - libname = WC2VSTR(bstr); - return libname; -} - -static VALUE -ole_types_from_typelib(ITypeLib *pTypeLib, VALUE classes) -{ - long count; - int i; - HRESULT hr; - BSTR bstr; - ITypeInfo *pTypeInfo; - VALUE type; - - count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib); - for (i = 0; i < count; i++) { - hr = pTypeLib->lpVtbl->GetDocumentation(pTypeLib, i, - &bstr, NULL, NULL, NULL); - if (FAILED(hr)) - continue; - - hr = pTypeLib->lpVtbl->GetTypeInfo(pTypeLib, i, &pTypeInfo); - if (FAILED(hr)) - continue; - - type = create_win32ole_type(pTypeInfo, WC2VSTR(bstr)); - - rb_ary_push(classes, type); - OLE_RELEASE(pTypeInfo); - } - return classes; -} - -static VALUE -typelib_file_from_typelib(VALUE ole) -{ - HKEY htypelib, hclsid, hversion, hlang; - double fver; - DWORD i, j, k; - LONG err; - BOOL found = FALSE; - VALUE typelib; - VALUE file = Qnil; - VALUE clsid; - VALUE ver; - VALUE lang; - - err = reg_open_key(HKEY_CLASSES_ROOT, "TypeLib", &htypelib); - if(err != ERROR_SUCCESS) { - return Qnil; - } - for(i = 0; !found; i++) { - clsid = reg_enum_key(htypelib, i); - if (clsid == Qnil) - break; - err = reg_open_vkey(htypelib, clsid, &hclsid); - if (err != ERROR_SUCCESS) - continue; - fver = 0; - for(j = 0; !found; j++) { - ver = reg_enum_key(hclsid, j); - if (ver == Qnil) - break; - err = reg_open_vkey(hclsid, ver, &hversion); - if (err != ERROR_SUCCESS || fver > atof(StringValuePtr(ver))) - continue; - fver = atof(StringValuePtr(ver)); - typelib = reg_get_val(hversion, NULL); - if (typelib == Qnil) - continue; - if (rb_str_cmp(typelib, ole) == 0) { - for(k = 0; !found; k++) { - lang = reg_enum_key(hversion, k); - if (lang == Qnil) - break; - err = reg_open_vkey(hversion, lang, &hlang); - if (err == ERROR_SUCCESS) { - if ((file = reg_get_typelib_file_path(hlang)) != Qnil) - found = TRUE; - RegCloseKey(hlang); - } - } - } - RegCloseKey(hversion); - } - RegCloseKey(hclsid); - } - RegCloseKey(htypelib); - return file; -} - -static VALUE -typelib_file_from_clsid(VALUE ole) -{ - HKEY hroot, hclsid; - LONG err; - VALUE typelib; - char path[MAX_PATH + 1]; - - err = reg_open_key(HKEY_CLASSES_ROOT, "CLSID", &hroot); - if (err != ERROR_SUCCESS) { - return Qnil; - } - err = reg_open_key(hroot, StringValuePtr(ole), &hclsid); - if (err != ERROR_SUCCESS) { - RegCloseKey(hroot); - return Qnil; - } - typelib = reg_get_val2(hclsid, "InprocServer32"); - RegCloseKey(hroot); - RegCloseKey(hclsid); - if (typelib != Qnil) { - ExpandEnvironmentStrings(StringValuePtr(typelib), path, sizeof(path)); - path[MAX_PATH] = '\0'; - typelib = rb_str_new2(path); - } - return typelib; -} - -VALUE -typelib_file(VALUE ole) -{ - VALUE file = typelib_file_from_clsid(ole); - if (file != Qnil) { - return file; - } - return typelib_file_from_typelib(ole); -} - - -/* - * call-seq: - * WIN32OLE_TYPELIB#ole_types -> The array of WIN32OLE_TYPE object included the type library. - * - * Returns the type library file path. - * - * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library') - * classes = tlib.ole_types.collect{|k| k.name} # -> ['AddIn', 'AddIns' ...] - */ -static VALUE -foletypelib_ole_types(VALUE self) -{ - ITypeLib *pTypeLib = NULL; - VALUE classes = rb_ary_new(); - pTypeLib = itypelib(self); - ole_types_from_typelib(pTypeLib, classes); - return classes; -} - -/* - * call-seq: - * WIN32OLE_TYPELIB#inspect -> String - * - * Returns the type library name with class name. - * - * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library') - * tlib.inspect # => "<#WIN32OLE_TYPELIB:Microsoft Excel 9.0 Object Library>" - */ -static VALUE -foletypelib_inspect(VALUE self) -{ - return default_inspect(self, "WIN32OLE_TYPELIB"); -} - -VALUE cWIN32OLE_TYPELIB; - -void -Init_win32ole_typelib(void) -{ - cWIN32OLE_TYPELIB = rb_define_class_under(cWIN32OLE, "Typelib", rb_cObject); - rb_define_const(rb_cObject, "WIN32OLE_TYPELIB", cWIN32OLE_TYPELIB); - rb_define_singleton_method(cWIN32OLE_TYPELIB, "typelibs", foletypelib_s_typelibs, 0); - rb_define_alloc_func(cWIN32OLE_TYPELIB, foletypelib_s_allocate); - rb_define_method(cWIN32OLE_TYPELIB, "initialize", foletypelib_initialize, -2); - rb_define_method(cWIN32OLE_TYPELIB, "guid", foletypelib_guid, 0); - rb_define_method(cWIN32OLE_TYPELIB, "name", foletypelib_name, 0); - rb_define_method(cWIN32OLE_TYPELIB, "version", foletypelib_version, 0); - rb_define_method(cWIN32OLE_TYPELIB, "major_version", foletypelib_major_version, 0); - rb_define_method(cWIN32OLE_TYPELIB, "minor_version", foletypelib_minor_version, 0); - rb_define_method(cWIN32OLE_TYPELIB, "path", foletypelib_path, 0); - rb_define_method(cWIN32OLE_TYPELIB, "ole_types", foletypelib_ole_types, 0); - rb_define_alias(cWIN32OLE_TYPELIB, "ole_classes", "ole_types"); - rb_define_method(cWIN32OLE_TYPELIB, "visible?", foletypelib_visible, 0); - rb_define_method(cWIN32OLE_TYPELIB, "library_name", foletypelib_library_name, 0); - rb_define_alias(cWIN32OLE_TYPELIB, "to_s", "name"); - rb_define_method(cWIN32OLE_TYPELIB, "inspect", foletypelib_inspect, 0); -} diff --git a/ext/win32ole/win32ole_typelib.h b/ext/win32ole/win32ole_typelib.h deleted file mode 100644 index 2c2730bb58..0000000000 --- a/ext/win32ole/win32ole_typelib.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef WIN32OLE_TYPELIB_H -#define WIN32OLE_TYPELIB_H 1 - -extern VALUE cWIN32OLE_TYPELIB; - -void Init_win32ole_typelib(void); -ITypeLib * itypelib(VALUE self); -VALUE typelib_file(VALUE ole); -VALUE create_win32ole_typelib(ITypeLib *pTypeLib); -VALUE ole_typelib_from_itypeinfo(ITypeInfo *pTypeInfo); -#endif diff --git a/ext/win32ole/win32ole_variable.c b/ext/win32ole/win32ole_variable.c deleted file mode 100644 index e7f58c891e..0000000000 --- a/ext/win32ole/win32ole_variable.c +++ /dev/null @@ -1,384 +0,0 @@ -#include "win32ole.h" - -struct olevariabledata { - ITypeInfo *pTypeInfo; - UINT index; -}; - -static void olevariable_free(void *ptr); -static size_t olevariable_size(const void *ptr); -static VALUE folevariable_name(VALUE self); -static VALUE ole_variable_ole_type(ITypeInfo *pTypeInfo, UINT var_index); -static VALUE folevariable_ole_type(VALUE self); -static VALUE ole_variable_ole_type_detail(ITypeInfo *pTypeInfo, UINT var_index); -static VALUE folevariable_ole_type_detail(VALUE self); -static VALUE ole_variable_value(ITypeInfo *pTypeInfo, UINT var_index); -static VALUE folevariable_value(VALUE self); -static VALUE ole_variable_visible(ITypeInfo *pTypeInfo, UINT var_index); -static VALUE folevariable_visible(VALUE self); -static VALUE ole_variable_kind(ITypeInfo *pTypeInfo, UINT var_index); -static VALUE folevariable_variable_kind(VALUE self); -static VALUE ole_variable_varkind(ITypeInfo *pTypeInfo, UINT var_index); -static VALUE folevariable_varkind(VALUE self); -static VALUE folevariable_inspect(VALUE self); - -static const rb_data_type_t olevariable_datatype = { - "win32ole_variable", - {NULL, olevariable_free, olevariable_size,}, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY -}; - -static void -olevariable_free(void *ptr) -{ - struct olevariabledata *polevar = ptr; - OLE_FREE(polevar->pTypeInfo); - free(polevar); -} - -static size_t -olevariable_size(const void *ptr) -{ - return ptr ? sizeof(struct olevariabledata) : 0; -} - -/* - * Document-class: WIN32OLE_VARIABLE - * - * <code>WIN32OLE_VARIABLE</code> objects represent OLE variable information. - */ - -VALUE -create_win32ole_variable(ITypeInfo *pTypeInfo, UINT index, VALUE name) -{ - struct olevariabledata *pvar; - VALUE obj = TypedData_Make_Struct(cWIN32OLE_VARIABLE, struct olevariabledata, - &olevariable_datatype, pvar); - pvar->pTypeInfo = pTypeInfo; - OLE_ADDREF(pTypeInfo); - pvar->index = index; - rb_ivar_set(obj, rb_intern("name"), name); - return obj; -} - -/* - * call-seq: - * WIN32OLE_VARIABLE#name - * - * Returns the name of variable. - * - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'XlSheetType') - * variables = tobj.variables - * variables.each do |variable| - * puts "#{variable.name}" - * end - * - * The result of above script is following: - * xlChart - * xlDialogSheet - * xlExcel4IntlMacroSheet - * xlExcel4MacroSheet - * xlWorksheet - * - */ -static VALUE -folevariable_name(VALUE self) -{ - return rb_ivar_get(self, rb_intern("name")); -} - -static VALUE -ole_variable_ole_type(ITypeInfo *pTypeInfo, UINT var_index) -{ - VARDESC *pVarDesc; - HRESULT hr; - VALUE type; - hr = pTypeInfo->lpVtbl->GetVarDesc(pTypeInfo, var_index, &pVarDesc); - if (FAILED(hr)) - ole_raise(hr, eWIN32OLERuntimeError, "failed to GetVarDesc"); - type = ole_typedesc2val(pTypeInfo, &(pVarDesc->elemdescVar.tdesc), Qnil); - pTypeInfo->lpVtbl->ReleaseVarDesc(pTypeInfo, pVarDesc); - return type; -} - -/* - * call-seq: - * WIN32OLE_VARIABLE#ole_type - * - * Returns OLE type string. - * - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'XlSheetType') - * variables = tobj.variables - * variables.each do |variable| - * puts "#{variable.ole_type} #{variable.name}" - * end - * - * The result of above script is following: - * INT xlChart - * INT xlDialogSheet - * INT xlExcel4IntlMacroSheet - * INT xlExcel4MacroSheet - * INT xlWorksheet - * - */ -static VALUE -folevariable_ole_type(VALUE self) -{ - struct olevariabledata *pvar; - TypedData_Get_Struct(self, struct olevariabledata, &olevariable_datatype, pvar); - return ole_variable_ole_type(pvar->pTypeInfo, pvar->index); -} - -static VALUE -ole_variable_ole_type_detail(ITypeInfo *pTypeInfo, UINT var_index) -{ - VARDESC *pVarDesc; - HRESULT hr; - VALUE type = rb_ary_new(); - hr = pTypeInfo->lpVtbl->GetVarDesc(pTypeInfo, var_index, &pVarDesc); - if (FAILED(hr)) - ole_raise(hr, eWIN32OLERuntimeError, "failed to GetVarDesc"); - ole_typedesc2val(pTypeInfo, &(pVarDesc->elemdescVar.tdesc), type); - pTypeInfo->lpVtbl->ReleaseVarDesc(pTypeInfo, pVarDesc); - return type; -} - -/* - * call-seq: - * WIN32OLE_VARIABLE#ole_type_detail - * - * Returns detail information of type. The information is array of type. - * - * tobj = WIN32OLE_TYPE.new('DirectX 7 for Visual Basic Type Library', 'D3DCLIPSTATUS') - * variable = tobj.variables.find {|variable| variable.name == 'lFlags'} - * tdetail = variable.ole_type_detail - * p tdetail # => ["USERDEFINED", "CONST_D3DCLIPSTATUSFLAGS"] - * - */ -static VALUE -folevariable_ole_type_detail(VALUE self) -{ - struct olevariabledata *pvar; - TypedData_Get_Struct(self, struct olevariabledata, &olevariable_datatype, pvar); - return ole_variable_ole_type_detail(pvar->pTypeInfo, pvar->index); -} - -static VALUE -ole_variable_value(ITypeInfo *pTypeInfo, UINT var_index) -{ - VARDESC *pVarDesc; - HRESULT hr; - VALUE val = Qnil; - hr = pTypeInfo->lpVtbl->GetVarDesc(pTypeInfo, var_index, &pVarDesc); - if (FAILED(hr)) - return Qnil; - if(pVarDesc->varkind == VAR_CONST) - val = ole_variant2val(V_UNION1(pVarDesc, lpvarValue)); - pTypeInfo->lpVtbl->ReleaseVarDesc(pTypeInfo, pVarDesc); - return val; -} - -/* - * call-seq: - * WIN32OLE_VARIABLE#value - * - * Returns value if value is exists. If the value does not exist, - * this method returns nil. - * - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'XlSheetType') - * variables = tobj.variables - * variables.each do |variable| - * puts "#{variable.name} #{variable.value}" - * end - * - * The result of above script is following: - * xlChart = -4109 - * xlDialogSheet = -4116 - * xlExcel4IntlMacroSheet = 4 - * xlExcel4MacroSheet = 3 - * xlWorksheet = -4167 - * - */ -static VALUE -folevariable_value(VALUE self) -{ - struct olevariabledata *pvar; - TypedData_Get_Struct(self, struct olevariabledata, &olevariable_datatype, pvar); - return ole_variable_value(pvar->pTypeInfo, pvar->index); -} - -static VALUE -ole_variable_visible(ITypeInfo *pTypeInfo, UINT var_index) -{ - VARDESC *pVarDesc; - HRESULT hr; - VALUE visible = Qfalse; - hr = pTypeInfo->lpVtbl->GetVarDesc(pTypeInfo, var_index, &pVarDesc); - if (FAILED(hr)) - return visible; - if (!(pVarDesc->wVarFlags & (VARFLAG_FHIDDEN | - VARFLAG_FRESTRICTED | - VARFLAG_FNONBROWSABLE))) { - visible = Qtrue; - } - pTypeInfo->lpVtbl->ReleaseVarDesc(pTypeInfo, pVarDesc); - return visible; -} - -/* - * call-seq: - * WIN32OLE_VARIABLE#visible? - * - * Returns true if the variable is public. - * - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'XlSheetType') - * variables = tobj.variables - * variables.each do |variable| - * puts "#{variable.name} #{variable.visible?}" - * end - * - * The result of above script is following: - * xlChart true - * xlDialogSheet true - * xlExcel4IntlMacroSheet true - * xlExcel4MacroSheet true - * xlWorksheet true - * - */ -static VALUE -folevariable_visible(VALUE self) -{ - struct olevariabledata *pvar; - TypedData_Get_Struct(self, struct olevariabledata, &olevariable_datatype, pvar); - return ole_variable_visible(pvar->pTypeInfo, pvar->index); -} - -static VALUE -ole_variable_kind(ITypeInfo *pTypeInfo, UINT var_index) -{ - VARDESC *pVarDesc; - HRESULT hr; - VALUE kind = rb_str_new2("UNKNOWN"); - hr = pTypeInfo->lpVtbl->GetVarDesc(pTypeInfo, var_index, &pVarDesc); - if (FAILED(hr)) - return kind; - switch(pVarDesc->varkind) { - case VAR_PERINSTANCE: - kind = rb_str_new2("PERINSTANCE"); - break; - case VAR_STATIC: - kind = rb_str_new2("STATIC"); - break; - case VAR_CONST: - kind = rb_str_new2("CONSTANT"); - break; - case VAR_DISPATCH: - kind = rb_str_new2("DISPATCH"); - break; - default: - break; - } - pTypeInfo->lpVtbl->ReleaseVarDesc(pTypeInfo, pVarDesc); - return kind; -} - -/* - * call-seq: - * WIN32OLE_VARIABLE#variable_kind - * - * Returns variable kind string. - * - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'XlSheetType') - * variables = tobj.variables - * variables.each do |variable| - * puts "#{variable.name} #{variable.variable_kind}" - * end - * - * The result of above script is following: - * xlChart CONSTANT - * xlDialogSheet CONSTANT - * xlExcel4IntlMacroSheet CONSTANT - * xlExcel4MacroSheet CONSTANT - * xlWorksheet CONSTANT - */ -static VALUE -folevariable_variable_kind(VALUE self) -{ - struct olevariabledata *pvar; - TypedData_Get_Struct(self, struct olevariabledata, &olevariable_datatype, pvar); - return ole_variable_kind(pvar->pTypeInfo, pvar->index); -} - -static VALUE -ole_variable_varkind(ITypeInfo *pTypeInfo, UINT var_index) -{ - VARDESC *pVarDesc; - HRESULT hr; - VALUE kind = Qnil; - hr = pTypeInfo->lpVtbl->GetVarDesc(pTypeInfo, var_index, &pVarDesc); - if (FAILED(hr)) - return kind; - pTypeInfo->lpVtbl->ReleaseVarDesc(pTypeInfo, pVarDesc); - kind = RB_INT2FIX(pVarDesc->varkind); - return kind; -} - -/* - * call-seq: - * WIN32OLE_VARIABLE#varkind - * - * Returns the number which represents variable kind. - * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'XlSheetType') - * variables = tobj.variables - * variables.each do |variable| - * puts "#{variable.name} #{variable.varkind}" - * end - * - * The result of above script is following: - * xlChart 2 - * xlDialogSheet 2 - * xlExcel4IntlMacroSheet 2 - * xlExcel4MacroSheet 2 - * xlWorksheet 2 - */ -static VALUE -folevariable_varkind(VALUE self) -{ - struct olevariabledata *pvar; - TypedData_Get_Struct(self, struct olevariabledata, &olevariable_datatype, pvar); - return ole_variable_varkind(pvar->pTypeInfo, pvar->index); -} - -/* - * call-seq: - * WIN32OLE_VARIABLE#inspect -> String - * - * Returns the OLE variable name and the value with class name. - * - */ -static VALUE -folevariable_inspect(VALUE self) -{ - VALUE v = rb_inspect(folevariable_value(self)); - VALUE n = folevariable_name(self); - VALUE detail = rb_sprintf("%"PRIsVALUE"=%"PRIsVALUE, n, v); - return make_inspect("WIN32OLE_VARIABLE", detail); -} - -VALUE cWIN32OLE_VARIABLE; - -void Init_win32ole_variable(void) -{ - cWIN32OLE_VARIABLE = rb_define_class_under(cWIN32OLE, "Variable", rb_cObject); - rb_define_const(rb_cObject, "WIN32OLE_VARIABLE", cWIN32OLE_VARIABLE); - rb_undef_alloc_func(cWIN32OLE_VARIABLE); - rb_define_method(cWIN32OLE_VARIABLE, "name", folevariable_name, 0); - rb_define_method(cWIN32OLE_VARIABLE, "ole_type", folevariable_ole_type, 0); - rb_define_method(cWIN32OLE_VARIABLE, "ole_type_detail", folevariable_ole_type_detail, 0); - rb_define_method(cWIN32OLE_VARIABLE, "value", folevariable_value, 0); - rb_define_method(cWIN32OLE_VARIABLE, "visible?", folevariable_visible, 0); - rb_define_method(cWIN32OLE_VARIABLE, "variable_kind", folevariable_variable_kind, 0); - rb_define_method(cWIN32OLE_VARIABLE, "varkind", folevariable_varkind, 0); - rb_define_method(cWIN32OLE_VARIABLE, "inspect", folevariable_inspect, 0); - rb_define_alias(cWIN32OLE_VARIABLE, "to_s", "name"); -} diff --git a/ext/win32ole/win32ole_variable.h b/ext/win32ole/win32ole_variable.h deleted file mode 100644 index 209613fd44..0000000000 --- a/ext/win32ole/win32ole_variable.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef WIN32OLE_VARIABLE_H -#define WIN32OLE_VARIABLE_H 1 - -extern VALUE cWIN32OLE_VARIABLE; -VALUE create_win32ole_variable(ITypeInfo *pTypeInfo, UINT index, VALUE name); -void Init_win32ole_variable(void); - -#endif diff --git a/ext/win32ole/win32ole_variant.c b/ext/win32ole/win32ole_variant.c deleted file mode 100644 index f1d83ed2e1..0000000000 --- a/ext/win32ole/win32ole_variant.c +++ /dev/null @@ -1,736 +0,0 @@ -#include "win32ole.h" - -struct olevariantdata { - VARIANT realvar; - VARIANT var; -}; - -static void olevariant_free(void *ptr); -static size_t olevariant_size(const void *ptr); -static void ole_val2olevariantdata(VALUE val, VARTYPE vt, struct olevariantdata *pvar); -static void ole_val2variant_err(VALUE val, VARIANT *var); -static void ole_set_byref(VARIANT *realvar, VARIANT *var, VARTYPE vt); -static VALUE folevariant_s_allocate(VALUE klass); -static VALUE folevariant_s_array(VALUE klass, VALUE dims, VALUE vvt); -static void check_type_val2variant(VALUE val); -static VALUE folevariant_initialize(VALUE self, VALUE args); -static LONG *ary2safe_array_index(int ary_size, VALUE *ary, SAFEARRAY *psa); -static void unlock_safe_array(SAFEARRAY *psa); -static SAFEARRAY *get_locked_safe_array(VALUE val); -static VALUE folevariant_ary_aref(int argc, VALUE *argv, VALUE self); -static VALUE folevariant_ary_aset(int argc, VALUE *argv, VALUE self); -static VALUE folevariant_value(VALUE self); -static VALUE folevariant_vartype(VALUE self); -static VALUE folevariant_set_value(VALUE self, VALUE val); - -static const rb_data_type_t olevariant_datatype = { - "win32ole_variant", - {NULL, olevariant_free, olevariant_size,}, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY -}; - -static void -olevariant_free(void *ptr) -{ - struct olevariantdata *pvar = ptr; - VariantClear(&(pvar->realvar)); - VariantClear(&(pvar->var)); - free(pvar); -} - -static size_t -olevariant_size(const void *ptr) -{ - return ptr ? sizeof(struct olevariantdata) : 0; -} - -static void -ole_val2olevariantdata(VALUE val, VARTYPE vt, struct olevariantdata *pvar) -{ - HRESULT hr = S_OK; - - if (((vt & ~VT_BYREF) == (VT_ARRAY | VT_UI1)) && RB_TYPE_P(val, T_STRING)) { - long len = RSTRING_LEN(val); - void *pdest = NULL; - SAFEARRAY *p = NULL; - SAFEARRAY *psa = SafeArrayCreateVector(VT_UI1, 0, len); - if (!psa) { - rb_raise(rb_eRuntimeError, "fail to SafeArrayCreateVector"); - } - hr = SafeArrayAccessData(psa, &pdest); - if (SUCCEEDED(hr)) { - memcpy(pdest, RSTRING_PTR(val), len); - SafeArrayUnaccessData(psa); - V_VT(&(pvar->realvar)) = (vt & ~VT_BYREF); - p = V_ARRAY(&(pvar->realvar)); - if (p != NULL) { - SafeArrayDestroy(p); - } - V_ARRAY(&(pvar->realvar)) = psa; - if (vt & VT_BYREF) { - V_VT(&(pvar->var)) = vt; - V_ARRAYREF(&(pvar->var)) = &(V_ARRAY(&(pvar->realvar))); - } else { - hr = VariantCopy(&(pvar->var), &(pvar->realvar)); - } - } else { - if (psa) - SafeArrayDestroy(psa); - } - } else if (vt & VT_ARRAY) { - if (val == Qnil) { - V_VT(&(pvar->var)) = vt; - if (vt & VT_BYREF) { - V_ARRAYREF(&(pvar->var)) = &(V_ARRAY(&(pvar->realvar))); - } - } else { - hr = ole_val_ary2variant_ary(val, &(pvar->realvar), (VARTYPE)(vt & ~VT_BYREF)); - if (SUCCEEDED(hr)) { - if (vt & VT_BYREF) { - V_VT(&(pvar->var)) = vt; - V_ARRAYREF(&(pvar->var)) = &(V_ARRAY(&(pvar->realvar))); - } else { - hr = VariantCopy(&(pvar->var), &(pvar->realvar)); - } - } - } -#if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) - } else if ( (vt & ~VT_BYREF) == VT_I8 || (vt & ~VT_BYREF) == VT_UI8) { - ole_val2variant_ex(val, &(pvar->realvar), (vt & ~VT_BYREF)); - ole_val2variant_ex(val, &(pvar->var), (vt & ~VT_BYREF)); - V_VT(&(pvar->var)) = vt; - if (vt & VT_BYREF) { - ole_set_byref(&(pvar->realvar), &(pvar->var), vt); - } -#endif - } else if ( (vt & ~VT_BYREF) == VT_ERROR) { - ole_val2variant_err(val, &(pvar->realvar)); - if (vt & VT_BYREF) { - ole_set_byref(&(pvar->realvar), &(pvar->var), vt); - } else { - hr = VariantCopy(&(pvar->var), &(pvar->realvar)); - } - } else { - if (val == Qnil) { - V_VT(&(pvar->var)) = vt; - if (vt == (VT_BYREF | VT_VARIANT)) { - ole_set_byref(&(pvar->realvar), &(pvar->var), vt); - } else { - V_VT(&(pvar->realvar)) = vt & ~VT_BYREF; - if (vt & VT_BYREF) { - ole_set_byref(&(pvar->realvar), &(pvar->var), vt); - } - } - } else { - ole_val2variant_ex(val, &(pvar->realvar), (VARTYPE)(vt & ~VT_BYREF)); - if (vt == (VT_BYREF | VT_VARIANT)) { - ole_set_byref(&(pvar->realvar), &(pvar->var), vt); - } else if (vt & VT_BYREF) { - if ( (vt & ~VT_BYREF) != V_VT(&(pvar->realvar))) { - hr = VariantChangeTypeEx(&(pvar->realvar), &(pvar->realvar), - cWIN32OLE_lcid, 0, (VARTYPE)(vt & ~VT_BYREF)); - } - if (SUCCEEDED(hr)) { - ole_set_byref(&(pvar->realvar), &(pvar->var), vt); - } - } else { - if (vt == V_VT(&(pvar->realvar))) { - hr = VariantCopy(&(pvar->var), &(pvar->realvar)); - } else { - hr = VariantChangeTypeEx(&(pvar->var), &(pvar->realvar), - cWIN32OLE_lcid, 0, vt); - } - } - } - } - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, "failed to change type"); - } -} - -static void -ole_val2variant_err(VALUE val, VARIANT *var) -{ - VALUE v = val; - if (rb_obj_is_kind_of(v, cWIN32OLE_VARIANT)) { - v = folevariant_value(v); - } - if (!(FIXNUM_P(v) || RB_TYPE_P(v, T_BIGNUM) || v == Qnil)) { - rb_raise(eWIN32OLERuntimeError, "failed to convert VT_ERROR VARIANT:`%"PRIsVALUE"'", rb_inspect(v)); - } - V_VT(var) = VT_ERROR; - if (v != Qnil) { - V_ERROR(var) = RB_NUM2LONG(val); - } else { - V_ERROR(var) = 0; - } -} - -static void -ole_set_byref(VARIANT *realvar, VARIANT *var, VARTYPE vt) -{ - V_VT(var) = vt; - if (vt == (VT_VARIANT|VT_BYREF)) { - V_VARIANTREF(var) = realvar; - } else { - if (V_VT(realvar) != (vt & ~VT_BYREF)) { - rb_raise(eWIN32OLERuntimeError, "variant type mismatch"); - } - switch(vt & ~VT_BYREF) { - case VT_I1: - V_I1REF(var) = &V_I1(realvar); - break; - case VT_UI1: - V_UI1REF(var) = &V_UI1(realvar); - break; - case VT_I2: - V_I2REF(var) = &V_I2(realvar); - break; - case VT_UI2: - V_UI2REF(var) = &V_UI2(realvar); - break; - case VT_I4: - V_I4REF(var) = &V_I4(realvar); - break; - case VT_UI4: - V_UI4REF(var) = &V_UI4(realvar); - break; - case VT_R4: - V_R4REF(var) = &V_R4(realvar); - break; - case VT_R8: - V_R8REF(var) = &V_R8(realvar); - break; - -#if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) -#ifdef V_I8REF - case VT_I8: - V_I8REF(var) = &V_I8(realvar); - break; -#endif -#ifdef V_UI8REF - case VT_UI8: - V_UI8REF(var) = &V_UI8(realvar); - break; -#endif -#endif - case VT_INT: - V_INTREF(var) = &V_INT(realvar); - break; - - case VT_UINT: - V_UINTREF(var) = &V_UINT(realvar); - break; - - case VT_CY: - V_CYREF(var) = &V_CY(realvar); - break; - case VT_DATE: - V_DATEREF(var) = &V_DATE(realvar); - break; - case VT_BSTR: - V_BSTRREF(var) = &V_BSTR(realvar); - break; - case VT_DISPATCH: - V_DISPATCHREF(var) = &V_DISPATCH(realvar); - break; - case VT_ERROR: - V_ERRORREF(var) = &V_ERROR(realvar); - break; - case VT_BOOL: - V_BOOLREF(var) = &V_BOOL(realvar); - break; - case VT_UNKNOWN: - V_UNKNOWNREF(var) = &V_UNKNOWN(realvar); - break; - case VT_ARRAY: - V_ARRAYREF(var) = &V_ARRAY(realvar); - break; - default: - rb_raise(eWIN32OLERuntimeError, "unknown type specified(setting BYREF):%d", vt); - break; - } - } -} - -static VALUE -folevariant_s_allocate(VALUE klass) -{ - struct olevariantdata *pvar; - VALUE obj; - ole_initialize(); - obj = TypedData_Make_Struct(klass, struct olevariantdata, &olevariant_datatype, pvar); - VariantInit(&(pvar->var)); - VariantInit(&(pvar->realvar)); - return obj; -} - -/* - * call-seq: - * WIN32OLE_VARIANT.array(ary, vt) - * - * Returns Ruby object wrapping OLE variant whose variant type is VT_ARRAY. - * The first argument should be Array object which specifies dimensions - * and each size of dimensions of OLE array. - * The second argument specifies variant type of the element of OLE array. - * - * The following create 2 dimensions OLE array. The first dimensions size - * is 3, and the second is 4. - * - * ole_ary = WIN32OLE_VARIANT.array([3,4], VT_I4) - * ruby_ary = ole_ary.value # => [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] - * - */ -static VALUE -folevariant_s_array(VALUE klass, VALUE elems, VALUE vvt) -{ - VALUE obj = Qnil; - VARTYPE vt; - struct olevariantdata *pvar; - SAFEARRAYBOUND *psab = NULL; - SAFEARRAY *psa = NULL; - UINT dim = 0; - UINT i = 0; - - ole_initialize(); - - vt = RB_NUM2UINT(vvt); - vt = (vt | VT_ARRAY); - Check_Type(elems, T_ARRAY); - obj = folevariant_s_allocate(klass); - - TypedData_Get_Struct(obj, struct olevariantdata, &olevariant_datatype, pvar); - dim = RARRAY_LEN(elems); - - psab = ALLOC_N(SAFEARRAYBOUND, dim); - - if(!psab) { - rb_raise(rb_eRuntimeError, "memory allocation error"); - } - - for (i = 0; i < dim; i++) { - psab[i].cElements = RB_FIX2INT(rb_ary_entry(elems, i)); - psab[i].lLbound = 0; - } - - psa = SafeArrayCreate((VARTYPE)(vt & VT_TYPEMASK), dim, psab); - if (psa == NULL) { - if (psab) free(psab); - rb_raise(rb_eRuntimeError, "memory allocation error(SafeArrayCreate)"); - } - - V_VT(&(pvar->var)) = vt; - if (vt & VT_BYREF) { - V_VT(&(pvar->realvar)) = (vt & ~VT_BYREF); - V_ARRAY(&(pvar->realvar)) = psa; - V_ARRAYREF(&(pvar->var)) = &(V_ARRAY(&(pvar->realvar))); - } else { - V_ARRAY(&(pvar->var)) = psa; - } - if (psab) free(psab); - return obj; -} - -static void -check_type_val2variant(VALUE val) -{ - VALUE elem; - int len = 0; - int i = 0; - if(!rb_obj_is_kind_of(val, cWIN32OLE) && - !rb_obj_is_kind_of(val, cWIN32OLE_VARIANT) && - !rb_obj_is_kind_of(val, rb_cTime)) { - switch (TYPE(val)) { - case T_ARRAY: - len = RARRAY_LEN(val); - for(i = 0; i < len; i++) { - elem = rb_ary_entry(val, i); - check_type_val2variant(elem); - } - break; - case T_STRING: - case T_FIXNUM: - case T_BIGNUM: - case T_FLOAT: - case T_TRUE: - case T_FALSE: - case T_NIL: - break; - default: - rb_raise(rb_eTypeError, "can not convert WIN32OLE_VARIANT from type %s", - rb_obj_classname(val)); - } - } -} - -/* - * Document-class: WIN32OLE_VARIANT - * - * <code>WIN32OLE_VARIANT</code> objects represents OLE variant. - * - * Win32OLE converts Ruby object into OLE variant automatically when - * invoking OLE methods. If OLE method requires the argument which is - * different from the variant by automatic conversion of Win32OLE, you - * can convert the specified variant type by using WIN32OLE_VARIANT class. - * - * param = WIN32OLE_VARIANT.new(10, WIN32OLE::VARIANT::VT_R4) - * oleobj.method(param) - * - * WIN32OLE_VARIANT does not support VT_RECORD variant. Use WIN32OLE_RECORD - * class instead of WIN32OLE_VARIANT if the VT_RECORD variant is needed. - */ - -/* - * call-seq: - * WIN32OLE_VARIANT.new(val, vartype) #=> WIN32OLE_VARIANT object. - * - * Returns Ruby object wrapping OLE variant. - * The first argument specifies Ruby object to convert OLE variant variable. - * The second argument specifies VARIANT type. - * In some situation, you need the WIN32OLE_VARIANT object to pass OLE method - * - * shell = WIN32OLE.new("Shell.Application") - * folder = shell.NameSpace("C:\\Windows") - * item = folder.ParseName("tmp.txt") - * # You can't use Ruby String object to call FolderItem.InvokeVerb. - * # Instead, you have to use WIN32OLE_VARIANT object to call the method. - * shortcut = WIN32OLE_VARIANT.new("Create Shortcut(\&S)") - * item.invokeVerb(shortcut) - * - */ -static VALUE -folevariant_initialize(VALUE self, VALUE args) -{ - int len = 0; - VARIANT var; - VALUE val; - VALUE vvt; - VARTYPE vt; - struct olevariantdata *pvar; - - len = RARRAY_LEN(args); - rb_check_arity(len, 1, 3); - VariantInit(&var); - val = rb_ary_entry(args, 0); - - check_type_val2variant(val); - - TypedData_Get_Struct(self, struct olevariantdata, &olevariant_datatype, pvar); - if (len == 1) { - ole_val2variant(val, &(pvar->var)); - } else { - vvt = rb_ary_entry(args, 1); - vt = RB_NUM2INT(vvt); - if ((vt & VT_TYPEMASK) == VT_RECORD) { - rb_raise(rb_eArgError, "not supported VT_RECORD WIN32OLE_VARIANT object"); - } - ole_val2olevariantdata(val, vt, pvar); - } - return self; -} - -static SAFEARRAY * -get_locked_safe_array(VALUE val) -{ - struct olevariantdata *pvar; - SAFEARRAY *psa = NULL; - HRESULT hr; - TypedData_Get_Struct(val, struct olevariantdata, &olevariant_datatype, pvar); - if (!(V_VT(&(pvar->var)) & VT_ARRAY)) { - rb_raise(rb_eTypeError, "variant type is not VT_ARRAY."); - } - psa = V_ISBYREF(&(pvar->var)) ? *V_ARRAYREF(&(pvar->var)) : V_ARRAY(&(pvar->var)); - if (psa == NULL) { - return psa; - } - hr = SafeArrayLock(psa); - if (FAILED(hr)) { - ole_raise(hr, rb_eRuntimeError, "failed to SafeArrayLock"); - } - return psa; -} - -static LONG * -ary2safe_array_index(int ary_size, VALUE *ary, SAFEARRAY *psa) -{ - long dim; - LONG *pid; - long i; - dim = SafeArrayGetDim(psa); - if (dim != ary_size) { - rb_raise(rb_eArgError, "unmatch number of indices"); - } - pid = ALLOC_N(LONG, dim); - if (pid == NULL) { - rb_raise(rb_eRuntimeError, "failed to allocate memory for indices"); - } - for (i = 0; i < dim; i++) { - pid[i] = RB_NUM2INT(ary[i]); - } - return pid; -} - -static void -unlock_safe_array(SAFEARRAY *psa) -{ - HRESULT hr; - hr = SafeArrayUnlock(psa); - if (FAILED(hr)) { - ole_raise(hr, rb_eRuntimeError, "failed to SafeArrayUnlock"); - } -} - -/* - * call-seq: - * WIN32OLE_VARIANT[i,j,...] #=> element of OLE array. - * - * Returns the element of WIN32OLE_VARIANT object(OLE array). - * This method is available only when the variant type of - * WIN32OLE_VARIANT object is VT_ARRAY. - * - * REMARK: - * The all indices should be 0 or natural number and - * lower than or equal to max indices. - * (This point is different with Ruby Array indices.) - * - * obj = WIN32OLE_VARIANT.new([[1,2,3],[4,5,6]]) - * p obj[0,0] # => 1 - * p obj[1,0] # => 4 - * p obj[2,0] # => WIN32OLERuntimeError - * p obj[0, -1] # => WIN32OLERuntimeError - * - */ -static VALUE -folevariant_ary_aref(int argc, VALUE *argv, VALUE self) -{ - struct olevariantdata *pvar; - SAFEARRAY *psa; - VALUE val = Qnil; - VARIANT variant; - LONG *pid; - HRESULT hr; - - TypedData_Get_Struct(self, struct olevariantdata, &olevariant_datatype, pvar); - if (!V_ISARRAY(&(pvar->var))) { - rb_raise(eWIN32OLERuntimeError, - "`[]' is not available for this variant type object"); - } - psa = get_locked_safe_array(self); - if (psa == NULL) { - return val; - } - - pid = ary2safe_array_index(argc, argv, psa); - - VariantInit(&variant); - V_VT(&variant) = (V_VT(&(pvar->var)) & ~VT_ARRAY) | VT_BYREF; - hr = SafeArrayPtrOfIndex(psa, pid, &V_BYREF(&variant)); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, "failed to SafeArrayPtrOfIndex"); - } - val = ole_variant2val(&variant); - - unlock_safe_array(psa); - if (pid) free(pid); - return val; -} - -/* - * call-seq: - * WIN32OLE_VARIANT[i,j,...] = val #=> set the element of OLE array - * - * Set the element of WIN32OLE_VARIANT object(OLE array) to val. - * This method is available only when the variant type of - * WIN32OLE_VARIANT object is VT_ARRAY. - * - * REMARK: - * The all indices should be 0 or natural number and - * lower than or equal to max indices. - * (This point is different with Ruby Array indices.) - * - * obj = WIN32OLE_VARIANT.new([[1,2,3],[4,5,6]]) - * obj[0,0] = 7 - * obj[1,0] = 8 - * p obj.value # => [[7,2,3], [8,5,6]] - * obj[2,0] = 9 # => WIN32OLERuntimeError - * obj[0, -1] = 9 # => WIN32OLERuntimeError - * - */ -static VALUE -folevariant_ary_aset(int argc, VALUE *argv, VALUE self) -{ - struct olevariantdata *pvar; - SAFEARRAY *psa; - VARIANT var; - VARTYPE vt; - LONG *pid; - HRESULT hr; - VOID *p = NULL; - - TypedData_Get_Struct(self, struct olevariantdata, &olevariant_datatype, pvar); - if (!V_ISARRAY(&(pvar->var))) { - rb_raise(eWIN32OLERuntimeError, - "`[]' is not available for this variant type object"); - } - psa = get_locked_safe_array(self); - if (psa == NULL) { - rb_raise(rb_eRuntimeError, "failed to get SafeArray pointer"); - } - - pid = ary2safe_array_index(argc-1, argv, psa); - - VariantInit(&var); - vt = (V_VT(&(pvar->var)) & ~VT_ARRAY); - p = val2variant_ptr(argv[argc-1], &var, vt); - if ((V_VT(&var) == VT_DISPATCH && V_DISPATCH(&var) == NULL) || - (V_VT(&var) == VT_UNKNOWN && V_UNKNOWN(&var) == NULL)) { - rb_raise(eWIN32OLERuntimeError, "argument does not have IDispatch or IUnknown Interface"); - } - hr = SafeArrayPutElement(psa, pid, p); - if (FAILED(hr)) { - ole_raise(hr, eWIN32OLERuntimeError, "failed to SafeArrayPutElement"); - } - - unlock_safe_array(psa); - if (pid) free(pid); - return argv[argc-1]; -} - -/* - * call-seq: - * WIN32OLE_VARIANT.value #=> Ruby object. - * - * Returns Ruby object value from OLE variant. - * obj = WIN32OLE_VARIANT.new(1, WIN32OLE::VARIANT::VT_BSTR) - * obj.value # => "1" (not Integer object, but String object "1") - * - */ -static VALUE -folevariant_value(VALUE self) -{ - struct olevariantdata *pvar; - VALUE val = Qnil; - VARTYPE vt; - int dim; - SAFEARRAY *psa; - TypedData_Get_Struct(self, struct olevariantdata, &olevariant_datatype, pvar); - - val = ole_variant2val(&(pvar->var)); - vt = V_VT(&(pvar->var)); - - if ((vt & ~VT_BYREF) == (VT_UI1|VT_ARRAY)) { - if (vt & VT_BYREF) { - psa = *V_ARRAYREF(&(pvar->var)); - } else { - psa = V_ARRAY(&(pvar->var)); - } - if (!psa) { - return val; - } - dim = SafeArrayGetDim(psa); - if (dim == 1) { - val = rb_funcall(val, rb_intern("pack"), 1, rb_str_new2("C*")); - } - } - return val; -} - -/* - * call-seq: - * WIN32OLE_VARIANT.vartype #=> OLE variant type. - * - * Returns OLE variant type. - * obj = WIN32OLE_VARIANT.new("string") - * obj.vartype # => WIN32OLE::VARIANT::VT_BSTR - * - */ -static VALUE -folevariant_vartype(VALUE self) -{ - struct olevariantdata *pvar; - TypedData_Get_Struct(self, struct olevariantdata, &olevariant_datatype, pvar); - return RB_INT2FIX(V_VT(&pvar->var)); -} - -/* - * call-seq: - * WIN32OLE_VARIANT.value = val #=> set WIN32OLE_VARIANT value to val. - * - * Sets variant value to val. If the val type does not match variant value - * type(vartype), then val is changed to match variant value type(vartype) - * before setting val. - * This method is not available when vartype is VT_ARRAY(except VT_UI1|VT_ARRAY). - * If the vartype is VT_UI1|VT_ARRAY, the val should be String object. - * - * obj = WIN32OLE_VARIANT.new(1) # obj.vartype is WIN32OLE::VARIANT::VT_I4 - * obj.value = 3.2 # 3.2 is changed to 3 when setting value. - * p obj.value # => 3 - */ -static VALUE -folevariant_set_value(VALUE self, VALUE val) -{ - struct olevariantdata *pvar; - VARTYPE vt; - TypedData_Get_Struct(self, struct olevariantdata, &olevariant_datatype, pvar); - vt = V_VT(&(pvar->var)); - if (V_ISARRAY(&(pvar->var)) && ((vt & ~VT_BYREF) != (VT_UI1|VT_ARRAY) || !RB_TYPE_P(val, T_STRING))) { - rb_raise(eWIN32OLERuntimeError, - "`value=' is not available for this variant type object"); - } - ole_val2olevariantdata(val, vt, pvar); - return Qnil; -} - -void -ole_variant2variant(VALUE val, VARIANT *var) -{ - struct olevariantdata *pvar; - TypedData_Get_Struct(val, struct olevariantdata, &olevariant_datatype, pvar); - VariantCopy(var, &(pvar->var)); -} - -VALUE cWIN32OLE_VARIANT; - -void -Init_win32ole_variant(void) -{ -#undef rb_intern - cWIN32OLE_VARIANT = rb_define_class_under(cWIN32OLE, "Variant", rb_cObject); - rb_define_const(rb_cObject, "WIN32OLE_VARIANT", cWIN32OLE_VARIANT); - rb_define_alloc_func(cWIN32OLE_VARIANT, folevariant_s_allocate); - rb_define_singleton_method(cWIN32OLE_VARIANT, "array", folevariant_s_array, 2); - rb_define_method(cWIN32OLE_VARIANT, "initialize", folevariant_initialize, -2); - rb_define_method(cWIN32OLE_VARIANT, "value", folevariant_value, 0); - rb_define_method(cWIN32OLE_VARIANT, "value=", folevariant_set_value, 1); - rb_define_method(cWIN32OLE_VARIANT, "vartype", folevariant_vartype, 0); - rb_define_method(cWIN32OLE_VARIANT, "[]", folevariant_ary_aref, -1); - rb_define_method(cWIN32OLE_VARIANT, "[]=", folevariant_ary_aset, -1); - - /* - * represents VT_EMPTY OLE object. - */ - rb_define_const(cWIN32OLE_VARIANT, "Empty", - rb_funcall(cWIN32OLE_VARIANT, rb_intern("new"), 2, Qnil, RB_INT2FIX(VT_EMPTY))); - - /* - * represents VT_NULL OLE object. - */ - rb_define_const(cWIN32OLE_VARIANT, "Null", - rb_funcall(cWIN32OLE_VARIANT, rb_intern("new"), 2, Qnil, RB_INT2FIX(VT_NULL))); - - /* - * represents Nothing of VB.NET or VB. - */ - rb_define_const(cWIN32OLE_VARIANT, "Nothing", - rb_funcall(cWIN32OLE_VARIANT, rb_intern("new"), 2, Qnil, RB_INT2FIX(VT_DISPATCH))); - - /* - * represents VT_ERROR variant with DISP_E_PARAMNOTFOUND. - * This constants is used for not specified parameter. - * - * fso = WIN32OLE.new("Scripting.FileSystemObject") - * fso.openTextFile(filename, WIN32OLE_VARIANT::NoParam, false) - */ - rb_define_const(cWIN32OLE_VARIANT, "NoParam", - rb_funcall(cWIN32OLE_VARIANT, rb_intern("new"), 2, INT2NUM(DISP_E_PARAMNOTFOUND), RB_INT2FIX(VT_ERROR))); -} diff --git a/ext/win32ole/win32ole_variant.h b/ext/win32ole/win32ole_variant.h deleted file mode 100644 index 4bd3b0aeea..0000000000 --- a/ext/win32ole/win32ole_variant.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef WIN32OLE_VARIANT_H -#define WIN32OLE_VARIANT_H 1 - -extern VALUE cWIN32OLE_VARIANT; -void ole_variant2variant(VALUE val, VARIANT *var); -void Init_win32ole_variant(void); - -#endif - diff --git a/ext/win32ole/win32ole_variant_m.c b/ext/win32ole/win32ole_variant_m.c deleted file mode 100644 index c285a00177..0000000000 --- a/ext/win32ole/win32ole_variant_m.c +++ /dev/null @@ -1,151 +0,0 @@ -#include "win32ole.h" - -VALUE mWIN32OLE_VARIANT; - -void Init_win32ole_variant_m(void) -{ - /* - * Document-module: WIN32OLE::VARIANT - * - * The WIN32OLE::VARIANT module includes constants of VARIANT type constants. - * The constants is used when creating WIN32OLE_VARIANT object. - * - * obj = WIN32OLE_VARIANT.new("2e3", WIN32OLE::VARIANT::VT_R4) - * obj.value # => 2000.0 - * - */ - mWIN32OLE_VARIANT = rb_define_module_under(cWIN32OLE, "VARIANT"); - - /* - * represents VT_EMPTY type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_EMPTY", RB_INT2FIX(VT_EMPTY)); - - /* - * represents VT_NULL type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_NULL", RB_INT2FIX(VT_NULL)); - - /* - * represents VT_I2 type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_I2", RB_INT2FIX(VT_I2)); - - /* - * represents VT_I4 type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_I4", RB_INT2FIX(VT_I4)); - - /* - * represents VT_R4 type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_R4", RB_INT2FIX(VT_R4)); - - /* - * represents VT_R8 type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_R8", RB_INT2FIX(VT_R8)); - - /* - * represents VT_CY type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_CY", RB_INT2FIX(VT_CY)); - - /* - * represents VT_DATE type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_DATE", RB_INT2FIX(VT_DATE)); - - /* - * represents VT_BSTR type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_BSTR", RB_INT2FIX(VT_BSTR)); - - /* - * represents VT_USERDEFINED type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_USERDEFINED", RB_INT2FIX(VT_USERDEFINED)); - - /* - * represents VT_PTR type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_PTR", RB_INT2FIX(VT_PTR)); - - /* - * represents VT_DISPATCH type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_DISPATCH", RB_INT2FIX(VT_DISPATCH)); - - /* - * represents VT_ERROR type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_ERROR", RB_INT2FIX(VT_ERROR)); - - /* - * represents VT_BOOL type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_BOOL", RB_INT2FIX(VT_BOOL)); - - /* - * represents VT_VARIANT type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_VARIANT", RB_INT2FIX(VT_VARIANT)); - - /* - * represents VT_UNKNOWN type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_UNKNOWN", RB_INT2FIX(VT_UNKNOWN)); - - /* - * represents VT_I1 type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_I1", RB_INT2FIX(VT_I1)); - - /* - * represents VT_UI1 type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_UI1", RB_INT2FIX(VT_UI1)); - - /* - * represents VT_UI2 type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_UI2", RB_INT2FIX(VT_UI2)); - - /* - * represents VT_UI4 type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_UI4", RB_INT2FIX(VT_UI4)); - -#if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) - /* - * represents VT_I8 type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_I8", RB_INT2FIX(VT_I8)); - - /* - * represents VT_UI8 type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_UI8", RB_INT2FIX(VT_UI8)); -#endif - - /* - * represents VT_INT type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_INT", RB_INT2FIX(VT_INT)); - - /* - * represents VT_UINT type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_UINT", RB_INT2FIX(VT_UINT)); - - /* - * represents VT_ARRAY type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_ARRAY", RB_INT2FIX(VT_ARRAY)); - - /* - * represents VT_BYREF type constant. - */ - rb_define_const(mWIN32OLE_VARIANT, "VT_BYREF", RB_INT2FIX(VT_BYREF)); - -} diff --git a/ext/win32ole/win32ole_variant_m.h b/ext/win32ole/win32ole_variant_m.h deleted file mode 100644 index 6272a6578f..0000000000 --- a/ext/win32ole/win32ole_variant_m.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef WIN32OLE_VARIANT_M_H -#define WIN32OLE_VARIANT_M_H 1 - -extern VALUE mWIN32OLE_VARIANT; -void Init_win32ole_variant_m(void); - -#endif diff --git a/ext/zlib/depend b/ext/zlib/depend index bdcf6a93e8..22e9ca867a 100644 --- a/ext/zlib/depend +++ b/ext/zlib/depend @@ -138,6 +138,7 @@ zlib.o: $(hdrdir)/ruby/internal/intern/re.h zlib.o: $(hdrdir)/ruby/internal/intern/ruby.h zlib.o: $(hdrdir)/ruby/internal/intern/select.h zlib.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +zlib.o: $(hdrdir)/ruby/internal/intern/set.h zlib.o: $(hdrdir)/ruby/internal/intern/signal.h zlib.o: $(hdrdir)/ruby/internal/intern/sprintf.h zlib.o: $(hdrdir)/ruby/internal/intern/string.h @@ -157,6 +158,7 @@ zlib.o: $(hdrdir)/ruby/internal/special_consts.h zlib.o: $(hdrdir)/ruby/internal/static_assert.h zlib.o: $(hdrdir)/ruby/internal/stdalign.h zlib.o: $(hdrdir)/ruby/internal/stdbool.h +zlib.o: $(hdrdir)/ruby/internal/stdckdint.h zlib.o: $(hdrdir)/ruby/internal/symbol.h zlib.o: $(hdrdir)/ruby/internal/value.h zlib.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index dc608ee460..7e319cae0d 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -25,7 +25,7 @@ # define VALGRIND_MAKE_MEM_UNDEFINED(p, n) 0 #endif -#define RUBY_ZLIB_VERSION "3.1.0" +#define RUBY_ZLIB_VERSION "3.2.2" #ifndef RB_PASS_CALLED_KEYWORDS # define rb_class_new_instance_kw(argc, argv, klass, kw_splat) rb_class_new_instance(argc, argv, klass) @@ -90,7 +90,7 @@ static void zstream_expand_buffer_into(struct zstream*, unsigned long); static int zstream_expand_buffer_non_stream(struct zstream *z); static void zstream_append_buffer(struct zstream*, const Bytef*, long); static VALUE zstream_detach_buffer(struct zstream*); -static VALUE zstream_shift_buffer(struct zstream*, long); +static VALUE zstream_shift_buffer(struct zstream*, long, VALUE); static void zstream_buffer_ungets(struct zstream*, const Bytef*, unsigned long); static void zstream_buffer_ungetbyte(struct zstream*, int); static void zstream_append_input(struct zstream*, const Bytef*, long); @@ -170,8 +170,8 @@ static void gzfile_check_footer(struct gzfile*, VALUE outbuf); static void gzfile_write(struct gzfile*, Bytef*, long); static long gzfile_read_more(struct gzfile*, VALUE outbuf); static void gzfile_calc_crc(struct gzfile*, VALUE); -static VALUE gzfile_read(struct gzfile*, long); -static VALUE gzfile_read_all(struct gzfile*); +static VALUE gzfile_read(struct gzfile*, long, VALUE); +static VALUE gzfile_read_all(struct gzfile*, VALUE); static void gzfile_ungets(struct gzfile*, const Bytef*, long); static void gzfile_ungetbyte(struct gzfile*, int); static VALUE gzfile_writer_end_run(VALUE); @@ -674,9 +674,7 @@ zstream_expand_buffer(struct zstream *z) rb_obj_reveal(z->buf, rb_cString); } - rb_mutex_unlock(z->mutex); - rb_protect(rb_yield, z->buf, &state); - rb_mutex_lock(z->mutex); + rb_protect(rb_yield, z->buf, &state); if (ZSTREAM_REUSE_BUFFER_P(z)) { rb_str_modify(z->buf); @@ -720,15 +718,14 @@ zstream_expand_buffer_into(struct zstream *z, unsigned long size) } } -static void * -zstream_expand_buffer_protect(void *ptr) +static int +zstream_expand_buffer_protect(struct zstream *z) { - struct zstream *z = (struct zstream *)ptr; int state = 0; rb_protect((VALUE (*)(VALUE))zstream_expand_buffer, (VALUE)z, &state); - return (void *)(VALUE)state; + return state; } static int @@ -820,19 +817,31 @@ zstream_detach_buffer(struct zstream *z) } static VALUE -zstream_shift_buffer(struct zstream *z, long len) +zstream_shift_buffer(struct zstream *z, long len, VALUE dst) { - VALUE dst; char *bufptr; long buflen = ZSTREAM_BUF_FILLED(z); if (buflen <= len) { - return zstream_detach_buffer(z); + if (NIL_P(dst) || (!ZSTREAM_IS_FINISHED(z) && !ZSTREAM_IS_GZFILE(z) && + rb_block_given_p())) { + return zstream_detach_buffer(z); + } else { + bufptr = RSTRING_PTR(z->buf); + rb_str_resize(dst, buflen); + memcpy(RSTRING_PTR(dst), bufptr, buflen); + } + buflen = 0; + } else { + bufptr = RSTRING_PTR(z->buf); + if (NIL_P(dst)) { + dst = rb_str_new(bufptr, len); + } else { + rb_str_resize(dst, len); + memcpy(RSTRING_PTR(dst), bufptr, len); + } + buflen -= len; } - - bufptr = RSTRING_PTR(z->buf); - dst = rb_str_new(bufptr, len); - buflen -= len; memmove(bufptr, bufptr + len, buflen); rb_str_set_len(z->buf, buflen); z->stream.next_out = (Bytef*)RSTRING_END(z->buf); @@ -1011,57 +1020,14 @@ zstream_ensure_end(VALUE v) } static void * -zstream_run_func(void *ptr) +zstream_run_once(void *_arguments) { - struct zstream_run_args *args = (struct zstream_run_args *)ptr; - int err, state, flush = args->flush; - struct zstream *z = args->z; - uInt n; - - err = Z_OK; - while (!args->interrupt) { - n = z->stream.avail_out; - err = z->func->run(&z->stream, flush); - rb_str_set_len(z->buf, ZSTREAM_BUF_FILLED(z) + (n - z->stream.avail_out)); + struct zstream_run_args *arguments = (struct zstream_run_args *)_arguments; + struct zstream *z = arguments->z; - if (err == Z_STREAM_END) { - z->flags &= ~ZSTREAM_FLAG_IN_STREAM; - z->flags |= ZSTREAM_FLAG_FINISHED; - break; - } + uintptr_t error = z->func->run(&z->stream, arguments->flush); - if (err != Z_OK && err != Z_BUF_ERROR) - break; - - if (z->stream.avail_out > 0) { - z->flags |= ZSTREAM_FLAG_IN_STREAM; - break; - } - - if (z->stream.avail_in == 0 && z->func == &inflate_funcs) { - /* break here because inflate() return Z_BUF_ERROR when avail_in == 0. */ - /* but deflate() could be called with avail_in == 0 (there's hidden buffer - in zstream->state) */ - z->flags |= ZSTREAM_FLAG_IN_STREAM; - break; - } - - if (args->stream_output) { - state = (int)(VALUE)rb_thread_call_with_gvl(zstream_expand_buffer_protect, - (void *)z); - } - else { - state = zstream_expand_buffer_non_stream(z); - } - - if (state) { - err = Z_OK; /* buffer expanded but stream processing was stopped */ - args->jump_state = state; - break; - } - } - - return (void *)(VALUE)err; + return (void*)error; } /* @@ -1076,6 +1042,91 @@ zstream_unblock_func(void *ptr) args->interrupt = 1; } +#ifndef RB_NOGVL_OFFLOAD_SAFE +// Default to no-op if it's not defined: +#define RB_NOGVL_OFFLOAD_SAFE 0 +#endif + +static VALUE +zstream_run_once_begin(VALUE _arguments) +{ + struct zstream_run_args *arguments = (struct zstream_run_args *)_arguments; + struct zstream *z = arguments->z; + + rb_str_locktmp(z->buf); + +#ifndef RB_NOGVL_UBF_ASYNC_SAFE + return (VALUE)rb_thread_call_without_gvl(zstream_run_once, (void *)arguments, zstream_unblock_func, (void *)arguments); +#else + return (VALUE)rb_nogvl(zstream_run_once, (void *)arguments, zstream_unblock_func, (void *)arguments, RB_NOGVL_UBF_ASYNC_SAFE | RB_NOGVL_OFFLOAD_SAFE); +#endif +} + +static VALUE +zstream_run_once_ensure(VALUE _arguments) +{ + struct zstream_run_args *arguments = (struct zstream_run_args *)_arguments; + struct zstream *z = arguments->z; + + rb_str_unlocktmp(z->buf); + + return Qnil; +} + +static int +zstream_run_func(struct zstream_run_args *args) +{ + struct zstream *z = args->z; + int state; + uInt n; + + int err = Z_OK; + while (!args->interrupt) { + n = z->stream.avail_out; + + err = (int)(VALUE)rb_ensure(zstream_run_once_begin, (VALUE)args, zstream_run_once_ensure, (VALUE)args); + + rb_str_set_len(z->buf, ZSTREAM_BUF_FILLED(z) + (n - z->stream.avail_out)); + + if (err == Z_STREAM_END) { + z->flags &= ~ZSTREAM_FLAG_IN_STREAM; + z->flags |= ZSTREAM_FLAG_FINISHED; + break; + } + + if (err != Z_OK && err != Z_BUF_ERROR) + break; + + if (z->stream.avail_out > 0) { + z->flags |= ZSTREAM_FLAG_IN_STREAM; + break; + } + + if (z->stream.avail_in == 0 && z->func == &inflate_funcs) { + /* break here because inflate() return Z_BUF_ERROR when avail_in == 0. */ + /* but deflate() could be called with avail_in == 0 (there's hidden buffer + in zstream->state) */ + z->flags |= ZSTREAM_FLAG_IN_STREAM; + break; + } + + if (args->stream_output) { + state = zstream_expand_buffer_protect(z); + } + else { + state = zstream_expand_buffer_non_stream(z); + } + + if (state) { + err = Z_OK; /* buffer expanded but stream processing was stopped */ + args->jump_state = state; + break; + } + } + + return err; +} + static VALUE zstream_run_try(VALUE value_arg) { @@ -1088,6 +1139,12 @@ zstream_run_try(VALUE value_arg) int err; VALUE old_input = Qnil; + /* Cannot start zstream while it is in progress. */ + if (z->flags & ZSTREAM_IN_PROGRESS) { + rb_raise(cInProgressError, "zlib stream is in progress"); + } + z->flags |= ZSTREAM_IN_PROGRESS; + if (NIL_P(z->input) && len == 0) { z->stream.next_in = (Bytef*)""; z->stream.avail_in = 0; @@ -1108,14 +1165,7 @@ zstream_run_try(VALUE value_arg) } loop: -#ifndef RB_NOGVL_UBF_ASYNC_SAFE - err = (int)(VALUE)rb_thread_call_without_gvl(zstream_run_func, (void *)args, - zstream_unblock_func, (void *)args); -#else - err = (int)(VALUE)rb_nogvl(zstream_run_func, (void *)args, - zstream_unblock_func, (void *)args, - RB_NOGVL_UBF_ASYNC_SAFE); -#endif + err = zstream_run_func(args); /* retry if no exception is thrown */ if (err == Z_OK && args->interrupt) { @@ -1155,9 +1205,6 @@ loop: rb_str_resize(old_input, 0); } - if (args->jump_state) - rb_jump_tag(args->jump_state); - return Qnil; } @@ -1165,25 +1212,10 @@ static VALUE zstream_run_ensure(VALUE value_arg) { struct zstream_run_args *args = (struct zstream_run_args *)value_arg; + struct zstream *z = args->z; /* Remove ZSTREAM_IN_PROGRESS flag to signal that this zstream is not in use. */ - args->z->flags &= ~ZSTREAM_IN_PROGRESS; - - return Qnil; -} - -static VALUE -zstream_run_synchronized(VALUE value_arg) -{ - struct zstream_run_args *args = (struct zstream_run_args *)value_arg; - - /* Cannot start zstream while it is in progress. */ - if (args->z->flags & ZSTREAM_IN_PROGRESS) { - rb_raise(cInProgressError, "zlib stream is in progress"); - } - args->z->flags |= ZSTREAM_IN_PROGRESS; - - rb_ensure(zstream_run_try, value_arg, zstream_run_ensure, value_arg); + z->flags &= ~ZSTREAM_IN_PROGRESS; return Qnil; } @@ -1200,7 +1232,10 @@ zstream_run(struct zstream *z, Bytef *src, long len, int flush) .jump_state = 0, .stream_output = !ZSTREAM_IS_GZFILE(z) && rb_block_given_p(), }; - rb_mutex_synchronize(z->mutex, zstream_run_synchronized, (VALUE)&args); + + rb_ensure(zstream_run_try, (VALUE)&args, zstream_run_ensure, (VALUE)&args); + if (args.jump_state) + rb_jump_tag(args.jump_state); } static VALUE @@ -1421,6 +1456,7 @@ rb_zstream_finish(VALUE obj) * call-seq: * flush_next_in -> input * + * Flushes input buffer and returns all data in that buffer. */ static VALUE rb_zstream_flush_next_in(VALUE obj) @@ -1509,7 +1545,7 @@ rb_zstream_total_out(VALUE obj) } /* - * Guesses the type of the data which have been inputed into the stream. The + * Guesses the type of the data which have been inputted into the stream. The * returned value is either <tt>BINARY</tt>, <tt>ASCII</tt>, or * <tt>UNKNOWN</tt>. */ @@ -1766,6 +1802,22 @@ do_deflate(struct zstream *z, VALUE src, int flush) } } +struct rb_zlib_deflate_arguments { + struct zstream *z; + VALUE src; + int flush; +}; + +static VALUE +rb_deflate_deflate_body(VALUE args) +{ + struct rb_zlib_deflate_arguments *arguments = (struct rb_zlib_deflate_arguments *)args; + + do_deflate(arguments->z, arguments->src, arguments->flush); + + return zstream_detach_buffer(arguments->z); +} + /* * Document-method: Zlib::Deflate#deflate * @@ -1797,11 +1849,10 @@ rb_deflate_deflate(int argc, VALUE *argv, VALUE obj) { struct zstream *z = get_zstream(obj); VALUE src, flush; - rb_scan_args(argc, argv, "11", &src, &flush); - do_deflate(z, src, ARG_FLUSH(flush)); + struct rb_zlib_deflate_arguments arguments = {z, src, ARG_FLUSH(flush)}; - return zstream_detach_buffer(z); + return rb_mutex_synchronize(z->mutex, rb_deflate_deflate_body, (VALUE)&arguments); } /* @@ -2097,56 +2148,19 @@ rb_inflate_add_dictionary(VALUE obj, VALUE dictionary) return obj; } -/* - * Document-method: Zlib::Inflate#inflate - * - * call-seq: - * inflate(deflate_string, buffer: nil) -> String - * inflate(deflate_string, buffer: nil) { |chunk| ... } -> nil - * - * Inputs +deflate_string+ into the inflate stream and returns the output from - * the stream. Calling this method, both the input and the output buffer of - * the stream are flushed. If string is +nil+, this method finishes the - * stream, just like Zlib::ZStream#finish. - * - * If a block is given consecutive inflated chunks from the +deflate_string+ - * are yielded to the block and +nil+ is returned. - * - * If a :buffer keyword argument is given and not nil: - * - * * The :buffer keyword should be a String, and will used as the output buffer. - * Using this option can reuse the memory required during inflation. - * * When not passing a block, the return value will be the same object as the - * :buffer keyword argument. - * * When passing a block, the yielded chunks will be the same value as the - * :buffer keyword argument. - * - * Raises a Zlib::NeedDict exception if a preset dictionary is needed to - * decompress. Set the dictionary by Zlib::Inflate#set_dictionary and then - * call this method again with an empty string to flush the stream: - * - * inflater = Zlib::Inflate.new - * - * begin - * out = inflater.inflate compressed - * rescue Zlib::NeedDict - * # ensure the dictionary matches the stream's required dictionary - * raise unless inflater.adler == Zlib.adler32(dictionary) - * - * inflater.set_dictionary dictionary - * inflater.inflate '' - * end - * - * # ... - * - * inflater.close - * - * See also Zlib::Inflate.new - */ +struct rb_zlib_inflate_arguments { + struct zstream *z; + int argc; + VALUE *argv; +}; + static VALUE -rb_inflate_inflate(int argc, VALUE* argv, VALUE obj) +rb_inflate_inflate_body(VALUE _arguments) { - struct zstream *z = get_zstream(obj); + struct rb_zlib_inflate_arguments *arguments = (struct rb_zlib_inflate_arguments*)_arguments; + struct zstream *z = arguments->z; + int argc = arguments->argc; + VALUE *argv = arguments->argv; VALUE dst, src, opts, buffer = Qnil; if (OPTHASH_GIVEN_P(opts)) { @@ -2202,6 +2216,60 @@ rb_inflate_inflate(int argc, VALUE* argv, VALUE obj) } /* + * Document-method: Zlib::Inflate#inflate + * + * call-seq: + * inflate(deflate_string, buffer: nil) -> String + * inflate(deflate_string, buffer: nil) { |chunk| ... } -> nil + * + * Inputs +deflate_string+ into the inflate stream and returns the output from + * the stream. Calling this method, both the input and the output buffer of + * the stream are flushed. If string is +nil+, this method finishes the + * stream, just like Zlib::ZStream#finish. + * + * If a block is given consecutive inflated chunks from the +deflate_string+ + * are yielded to the block and +nil+ is returned. + * + * If a :buffer keyword argument is given and not nil: + * + * * The :buffer keyword should be a String, and will used as the output buffer. + * Using this option can reuse the memory required during inflation. + * * When not passing a block, the return value will be the same object as the + * :buffer keyword argument. + * * When passing a block, the yielded chunks will be the same value as the + * :buffer keyword argument. + * + * Raises a Zlib::NeedDict exception if a preset dictionary is needed to + * decompress. Set the dictionary by Zlib::Inflate#set_dictionary and then + * call this method again with an empty string to flush the stream: + * + * inflater = Zlib::Inflate.new + * + * begin + * out = inflater.inflate compressed + * rescue Zlib::NeedDict + * # ensure the dictionary matches the stream's required dictionary + * raise unless inflater.adler == Zlib.adler32(dictionary) + * + * inflater.set_dictionary dictionary + * inflater.inflate '' + * end + * + * # ... + * + * inflater.close + * + * See also Zlib::Inflate.new + */ +static VALUE +rb_inflate_inflate(int argc, VALUE* argv, VALUE obj) +{ + struct zstream *z = get_zstream(obj); + struct rb_zlib_inflate_arguments arguments = {z, argc, argv}; + return rb_mutex_synchronize(z->mutex, rb_inflate_inflate_body, (VALUE)&arguments); +} + +/* * call-seq: << string * * Inputs +string+ into the inflate stream just like Zlib::Inflate#inflate, but @@ -2377,17 +2445,16 @@ struct gzfile { #define GZFILE_READ_SIZE 2048 +enum { read_raw_arg_len, read_raw_arg_buf, read_raw_arg__count}; struct read_raw_arg { VALUE io; - union { - const VALUE argv[2]; /* for rb_funcallv */ - struct { - VALUE len; - VALUE buf; - } in; - } as; + const VALUE argv[read_raw_arg__count]; /* for rb_funcallv */ }; +#define read_raw_arg_argc(ra) \ + ((int)read_raw_arg__count - NIL_P((ra)->argv[read_raw_arg__count - 1])) +#define read_raw_arg_init(io, len, buf) { io, { len, buf } } + static void gzfile_mark(void *p) { @@ -2513,9 +2580,9 @@ gzfile_read_raw_partial(VALUE arg) { struct read_raw_arg *ra = (struct read_raw_arg *)arg; VALUE str; - int argc = NIL_P(ra->as.argv[1]) ? 1 : 2; + int argc = read_raw_arg_argc(ra); - str = rb_funcallv(ra->io, id_readpartial, argc, ra->as.argv); + str = rb_funcallv(ra->io, id_readpartial, argc, ra->argv); Check_Type(str, T_STRING); return str; } @@ -2526,8 +2593,8 @@ gzfile_read_raw_rescue(VALUE arg, VALUE _) struct read_raw_arg *ra = (struct read_raw_arg *)arg; VALUE str = Qnil; if (rb_obj_is_kind_of(rb_errinfo(), rb_eNoMethodError)) { - int argc = NIL_P(ra->as.argv[1]) ? 1 : 2; - str = rb_funcallv(ra->io, id_read, argc, ra->as.argv); + int argc = read_raw_arg_argc(ra); + str = rb_funcallv(ra->io, id_read, argc, ra->argv); if (!NIL_P(str)) { Check_Type(str, T_STRING); } @@ -2538,11 +2605,8 @@ gzfile_read_raw_rescue(VALUE arg, VALUE _) static VALUE gzfile_read_raw(struct gzfile *gz, VALUE outbuf) { - struct read_raw_arg ra; - - ra.io = gz->io; - ra.as.in.len = INT2FIX(GZFILE_READ_SIZE); - ra.as.in.buf = outbuf; + struct read_raw_arg ra = + read_raw_arg_init(gz->io, INT2FIX(GZFILE_READ_SIZE), outbuf); return rb_rescue2(gzfile_read_raw_partial, (VALUE)&ra, gzfile_read_raw_rescue, (VALUE)&ra, @@ -2874,18 +2938,18 @@ gzfile_newstr(struct gzfile *gz, VALUE str) } static long -gzfile_fill(struct gzfile *gz, long len) +gzfile_fill(struct gzfile *gz, long len, VALUE outbuf) { if (len < 0) rb_raise(rb_eArgError, "negative length %ld given", len); if (len == 0) return 0; while (!ZSTREAM_IS_FINISHED(&gz->z) && ZSTREAM_BUF_FILLED(&gz->z) < len) { - gzfile_read_more(gz, Qnil); + gzfile_read_more(gz, outbuf); } if (GZFILE_IS_FINISHED(gz)) { if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) { - gzfile_check_footer(gz, Qnil); + gzfile_check_footer(gz, outbuf); } return -1; } @@ -2893,14 +2957,27 @@ gzfile_fill(struct gzfile *gz, long len) } static VALUE -gzfile_read(struct gzfile *gz, long len) +gzfile_read(struct gzfile *gz, long len, VALUE outbuf) { VALUE dst; - len = gzfile_fill(gz, len); - if (len == 0) return rb_str_new(0, 0); - if (len < 0) return Qnil; - dst = zstream_shift_buffer(&gz->z, len); + len = gzfile_fill(gz, len, outbuf); + + if (len < 0) { + if (!NIL_P(outbuf)) + rb_str_resize(outbuf, 0); + return Qnil; + } + if (len == 0) { + if (NIL_P(outbuf)) + return rb_str_new(0, 0); + else { + rb_str_resize(outbuf, 0); + return outbuf; + } + } + + dst = zstream_shift_buffer(&gz->z, len, outbuf); if (!NIL_P(dst)) gzfile_calc_crc(gz, dst); return dst; } @@ -2933,29 +3010,26 @@ gzfile_readpartial(struct gzfile *gz, long len, VALUE outbuf) rb_raise(rb_eEOFError, "end of file reached"); } - dst = zstream_shift_buffer(&gz->z, len); + dst = zstream_shift_buffer(&gz->z, len, outbuf); gzfile_calc_crc(gz, dst); - if (!NIL_P(outbuf)) { - rb_str_resize(outbuf, RSTRING_LEN(dst)); - memcpy(RSTRING_PTR(outbuf), RSTRING_PTR(dst), RSTRING_LEN(dst)); - dst = outbuf; - } return dst; } static VALUE -gzfile_read_all(struct gzfile *gz) +gzfile_read_all(struct gzfile *gz, VALUE dst) { - VALUE dst; - while (!ZSTREAM_IS_FINISHED(&gz->z)) { - gzfile_read_more(gz, Qnil); + gzfile_read_more(gz, dst); } if (GZFILE_IS_FINISHED(gz)) { if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) { - gzfile_check_footer(gz, Qnil); + gzfile_check_footer(gz, dst); } + if (!NIL_P(dst)) { + rb_str_resize(dst, 0); + return dst; + } return rb_str_new(0, 0); } @@ -2993,7 +3067,7 @@ gzfile_getc(struct gzfile *gz) de = (unsigned char *)ds + GZFILE_CBUF_CAPA; (void)rb_econv_convert(gz->ec, &sp, se, &dp, de, ECONV_PARTIAL_INPUT|ECONV_AFTER_OUTPUT); rb_econv_check_error(gz->ec); - dst = zstream_shift_buffer(&gz->z, sp - ss); + dst = zstream_shift_buffer(&gz->z, sp - ss, Qnil); gzfile_calc_crc(gz, dst); rb_str_resize(cbuf, dp - ds); return cbuf; @@ -3001,7 +3075,7 @@ gzfile_getc(struct gzfile *gz) else { buf = gz->z.buf; len = rb_enc_mbclen(RSTRING_PTR(buf), RSTRING_END(buf), gz->enc); - dst = gzfile_read(gz, len); + dst = gzfile_read(gz, len, Qnil); if (NIL_P(dst)) return dst; return gzfile_newstr(gz, dst); } @@ -3500,6 +3574,9 @@ static VALUE rb_gzfile_eof_p(VALUE obj) { struct gzfile *gz = get_gzfile(obj); + while (!ZSTREAM_IS_FINISHED(&gz->z) && ZSTREAM_BUF_FILLED(&gz->z) == 0) { + gzfile_read_more(gz, Qnil); + } return GZFILE_IS_FINISHED(gz) ? Qtrue : Qfalse; } @@ -3906,7 +3983,7 @@ rb_gzreader_s_zcat(int argc, VALUE *argv, VALUE klass) if (!buf) { buf = rb_str_new(0, 0); } - tmpbuf = gzfile_read_all(get_gzfile(obj)); + tmpbuf = gzfile_read_all(get_gzfile(obj), Qnil); rb_str_cat(buf, RSTRING_PTR(tmpbuf), RSTRING_LEN(tmpbuf)); } @@ -4008,19 +4085,19 @@ static VALUE rb_gzreader_read(int argc, VALUE *argv, VALUE obj) { struct gzfile *gz = get_gzfile(obj); - VALUE vlen; + VALUE vlen, outbuf; long len; - rb_scan_args(argc, argv, "01", &vlen); + rb_scan_args(argc, argv, "02", &vlen, &outbuf); if (NIL_P(vlen)) { - return gzfile_read_all(gz); + return gzfile_read_all(gz, outbuf); } len = NUM2INT(vlen); if (len < 0) { rb_raise(rb_eArgError, "negative length %ld given", len); } - return gzfile_read(gz, len); + return gzfile_read(gz, len, outbuf); } /* @@ -4029,7 +4106,7 @@ rb_gzreader_read(int argc, VALUE *argv, VALUE obj) * call-seq: * gzipreader.readpartial(maxlen [, outbuf]) => string, outbuf * - * Reads at most <i>maxlen</i> bytes from the gziped stream but + * Reads at most <i>maxlen</i> bytes from the gzipped stream but * it blocks only if <em>gzipreader</em> has no data immediately available. * If the optional <i>outbuf</i> argument is present, * it must reference a String, which will receive the data. @@ -4093,7 +4170,7 @@ rb_gzreader_getbyte(VALUE obj) struct gzfile *gz = get_gzfile(obj); VALUE dst; - dst = gzfile_read(gz, 1); + dst = gzfile_read(gz, 1, Qnil); if (!NIL_P(dst)) { dst = INT2FIX((unsigned int)(RSTRING_PTR(dst)[0]) & 0xff); } @@ -4204,6 +4281,7 @@ gzreader_skip_linebreaks(struct gzfile *gz) while (n++, *(p++) == '\n') { if (n >= ZSTREAM_BUF_FILLED(&gz->z)) { str = zstream_detach_buffer(&gz->z); + ASSUME(!NIL_P(str)); gzfile_calc_crc(gz, str); while (ZSTREAM_BUF_FILLED(&gz->z) == 0) { if (GZFILE_IS_FINISHED(gz)) return; @@ -4214,7 +4292,7 @@ gzreader_skip_linebreaks(struct gzfile *gz) } } - str = zstream_shift_buffer(&gz->z, n - 1); + str = zstream_shift_buffer(&gz->z, n - 1, Qnil); gzfile_calc_crc(gz, str); } @@ -4235,7 +4313,7 @@ gzreader_charboundary(struct gzfile *gz, long n) if (l < n) { int n_bytes = rb_enc_precise_mbclen(p, e, gz->enc); if (MBCLEN_NEEDMORE_P(n_bytes)) { - if ((l = gzfile_fill(gz, n + MBCLEN_NEEDMORE_LEN(n_bytes))) > 0) { + if ((l = gzfile_fill(gz, n + MBCLEN_NEEDMORE_LEN(n_bytes), Qnil)) > 0) { return l; } } @@ -4287,10 +4365,10 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) if (NIL_P(rs)) { if (limit < 0) { - dst = gzfile_read_all(gz); + dst = gzfile_read_all(gz, Qnil); if (RSTRING_LEN(dst) == 0) return Qnil; } - else if ((n = gzfile_fill(gz, limit)) <= 0) { + else if ((n = gzfile_fill(gz, limit, Qnil)) <= 0) { return Qnil; } else { @@ -4300,7 +4378,7 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) else { n = limit; } - dst = zstream_shift_buffer(&gz->z, n); + dst = zstream_shift_buffer(&gz->z, n, Qnil); if (NIL_P(dst)) return dst; gzfile_calc_crc(gz, dst); dst = gzfile_newstr(gz, dst); @@ -4327,7 +4405,7 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) while (ZSTREAM_BUF_FILLED(&gz->z) < rslen) { if (ZSTREAM_IS_FINISHED(&gz->z)) { if (ZSTREAM_BUF_FILLED(&gz->z) > 0) gz->lineno++; - return gzfile_read(gz, rslen); + return gzfile_read(gz, rslen, Qnil); } gzfile_read_more(gz, Qnil); } @@ -4364,7 +4442,7 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) } gz->lineno++; - dst = gzfile_read(gz, n); + dst = gzfile_read(gz, n, Qnil); if (NIL_P(dst)) return dst; if (rspara) { gzreader_skip_linebreaks(gz); @@ -4612,6 +4690,7 @@ zlib_gunzip_run(VALUE arg) gzfile_read_header(gz, Qnil); dst = zstream_detach_buffer(&gz->z); + ASSUME(!NIL_P(dst)); gzfile_calc_crc(gz, dst); if (!ZSTREAM_IS_FINISHED(&gz->z)) { rb_raise(cGzError, "unexpected end of file"); diff --git a/ext/zlib/zlib.gemspec b/ext/zlib/zlib.gemspec index bb67ea156c..345dc5f225 100644 --- a/ext/zlib/zlib.gemspec +++ b/ext/zlib/zlib.gemspec @@ -22,7 +22,7 @@ Gem::Specification.new do |spec| spec.homepage = "https://github.com/ruby/zlib" spec.licenses = ["Ruby", "BSD-2-Clause"] - spec.files = ["LICENSE.txt", "README.md", "ext/zlib/extconf.rb", "ext/zlib/zlib.c", "zlib.gemspec"] + spec.files = ["COPYING", "BSDL", "README.md", "ext/zlib/extconf.rb", "ext/zlib/zlib.c", "zlib.gemspec"] spec.bindir = "exe" spec.executables = [] spec.require_paths = ["lib"] |
